[Tema de primavera] Análisis del código fuente del ciclo de vida del frijol en primavera: fase dos (3) (análisis del principio subyacente de dependencia circular para el llenado de atributos)

prefacio

preparación de lectura

Revisión anterior:

consejos de lectura

  1. Mire el código fuente y recuerde enredarse en los detalles, de lo contrario es fácil quedarse atascado. Normalmente, basta con mirar el proceso principal.
  2. Si no lo comprende, consulte las anotaciones de clases o las anotaciones de métodos. Spring es un código fuente excelente, las anotaciones están realmente en su lugar.
  3. Si es un usuario de ideas, utilice más la función de marcador de F11.
    • Ctrl + F11 Seleccionar archivos/carpetas, usar mnemotécnicos para establecer/cancelar marcadores (obligatorio)
    • Mayús + F11 Capa de visualización de marcadores emergentes (obligatorio)
    • Ctrl +1,2,3...9 Ubicar en la posición del marcador del valor correspondiente (obligatorio)

Conocimiento previo

ciclo de vida del frijol

El ciclo de vida de un bean se refiere a: ¿cómo se genera un bean en primavera? Los pasos para la generación de Bean son los siguientes: (PD: aquí no describirá el ciclo de vida de Bean en detalle, solo describirá el proceso general)

  1. Crear instancias de beans según beanDefinition
  2. Complete las propiedades en el objeto original (Inyección de dependencia)
  3. antes de la inicialización
  4. inicialización
  5. después de la inicialización
  6. Coloque el bean generado en el grupo singleton.
    Coloque el objeto proxy generado final en el grupo singleton (llamado singletonObjects en el código fuente) y tómelo directamente del grupo singleton la próxima vez que obtenga Bean.

Generación de dependencias circulares

Cuando se trata de dependencias circulares, todo el mundo está familiarizado con ellas. El código para las dependencias circulares es el siguiente:

@Component
public class CircularA {
    
    

    @Autowired
    CircularB b;
}

@Component
public class CircularB {
    
    

    @Autowired
    CircularA a;
}

Pero ¿alguna vez has pensado en cómo se generan las dependencias circulares y cómo solucionarlas? Aquí quiero darles una deducción, tal como somos autores de Spring, pensando en cómo resolver dependencias circulares.

Tres mapas en primavera

Aquí, todavía quiero brindarles una introducción general por adelantado, al obtener un bean singleton, cuáles son los tres mapas que aparecen en el código fuente de Spring y qué se usa para almacenarlos. Son los siguientes:

  • Map<String, Object> singletonObjects: Caché de nivel 1. Esto es lo que a menudo llamamos grupo singleton, los beans almacenados aquí,Ha pasado por el ciclo de vida completo de Spring, [completó el ciclo de vida diseñado por Spring](Experimentar el ciclo de vida completo aquí no significa que tenga que pasar por antes y después de la creación de instancias, antes y después de la inicialización. En pocas palabras, es: Bean maduro y aprobado por Spring)
  • Map<String, Object> earlySingletonObjects: Caché de segundo nivel. Traducido directamente, lo que se almacena aquí es [Early Singleton Bean]. ¿Qué es temprano? Es relativo al [Frijol Maduro] anterior,[Frijol que aún no ha completado su ciclo de vida]
  • Map<String, ObjectFactory<?>> singletonFactories: Caché L3. La traducción literal es [fábrica de frijoles singleton]. De hecho, todavía me gusta usar un nombre propio mencionado antes para explicar:Almacenamiento en caché del método Hook de beans de producción

Contenido del curso

Nota: El diagrama de flujo del ciclo de vida del frijol en la figura siguiente no representa el ciclo real. Por conveniencia, solo realicé algunos procesos.

1. Razonamiento evolutivo [caché de nivel 3]

1. Solo el razonamiento evolutivo del caché de primer nivel.

Primero veamos una imagen: antes no había caché de tercer nivel, cuando solo había un caché de primer nivel, si A depende de B y B depende de A, ocurrirá el siguiente fenómeno:
inserte la descripción de la imagen aquí

Obviamente, en el proceso de nuestra creación inicial, no habrá ni el objeto B ni el objeto A en el grupo singleton. Después de todo, solo llegaron al segundo paso [inyectar atributos], y es en el último paso donde colocan los objetos generados en el grupo singleton. Por lo tanto, en la situación de la imagen de arriba, si no hay intervención externa, se forma un bucle cerrado entre los dos frijoles, que no se puede desatar. Obviamente este no es el resultado que queremos, ¿verdad? Entonces, ¿cómo solucionar este problema?

1.1 Coloque directamente el objeto generado después de la creación de instancias en el grupo singleton

En este momento, un pensamiento muy normal es: ¿no estaría bien si lo pusiera en el grupo singleton con anticipación, como se muestra a continuación: ¿
inserte la descripción de la imagen aquí
No está roto? Jejeje,
sólo puedo decir que tiene sentido, pero no mucho. Debido a que Spring en realidad toma objetos del grupo singleton, hacerlo equivale a exponer [ objetos semiacabados que no han completado su ciclo de vida ] por adelantado . De esta manera, en un entorno de subprocesos múltiples, si alguien accede al grupo singleton, obtiene directamente este BeanA y luego llama al método que contiene, si no hay [inyección de propiedad], ¿no sería G? ¡Sí, ese es un problema de seguridad de concurrencia! Aquí solo podemos pasar este esquema directamente.
PD: Por supuesto, sé que algunas personas dirán que bloquear el caché de primer nivel puede resolverlo, ah, sí, puede. Pero ¿alguna vez te has preguntado qué pasa con el rendimiento...?

1.2 Resumen

  1. Después de la creación de instancias, [ los objetos semiacabados que no han completado su ciclo de vida ] se colocan en el grupo singleton, lo que provocará problemas de seguridad de subprocesos.
    (PD: ¡¡¡Presta atención a la conclusión aquí, se probará más tarde !!! /狗头/狗头)

2. Presentación del razonamiento evolutivo del caché de segundo nivel.

2.1 Introducir un objeto temprano después de la creación de instancias de un almacenamiento de mapas intermedio (se sospecha que es un caché de segundo nivel)

Un pensamiento muy normal: agrego un nuevo mapa y lo guardo inmediatamente después de crear una instancia. De todos modos, ya se ha creado una instancia y la dirección ha sido fijada. No importa cómo opere más adelante, operará en el objeto en esta dirección. Exponer este objeto con anticipación no afectará el resultado en absoluto.
inserte la descripción de la imagen aquí
Como se muestra en la figura anterior, luego agrego un mapa de caché intermedio para almacenar los objetos instanciados previamente, ¿es posible? Bueno, a juzgar por el diagrama de flujo, esta realmente parece ser la respuesta final.
Sin embargo, si te pregunto [qué hacer con AOP] o, para ser precisos, lo que necesitas es [qué hacer con el objeto proxy], ¿cómo responderás?? Obviamente, esta tabla intermedia almacena el objeto original, pero a veces lo que necesito es un objeto proxy. Mira, después de un pequeño escrutinio, hay otro problema. Bueno, sigamos mejorando este plan.
(PD: esta pregunta significa que debemos considerar el proxy AOP de antemano en este paso. Todos deberían recordar esta conclusión)
(Nota: solo estoy dando un ejemplo de la necesidad de AOP. De hecho, se refiere a cualquier proceso que requiera un proxy. ¿Crees que existe una situación de proxy multinivel?)
(Nota: solo estoy dando un ejemplo de la necesidad de AOP. De hecho, se refiere a cualquier proceso que requiera un proxy. ¿Crees que existe una situación de proxy multinivel?)
(Nota: solo estoy dando un ejemplo de la necesidad de AOP. De hecho, se refiere a cualquier proceso que requiera un proxy. ¿Crees que existe una situación de proxy multinivel?)

2.2 Resuelva el problema de la necesidad de ser proxy en 2.1 (se sospecha que se almacena caché de segundo nivel)

inserte la descripción de la imagen aquí

Eso es todo, solo agrega un paso más al proceso de AOP, jejeje. Pero según la práctica habitual, ya lo he hecho [jejeje], así que debo preguntar: ¿Está realmente bien? ¡Ja, de verdad! Está realmente bien. Entonces, ¿por qué necesitamos un caché de tercer nivel?

3. Presentación del razonamiento evolutivo del caché de tercer nivel.

3.1 Por qué la caché L3

En este punto, estoy a punto de empezar a fingir. (Incluso sospecho que Spring pretende escribir así, jaja, es broma) De hecho, hay
inserte la descripción de la imagen aquí
muchos argumentos en Internet. También resumí las fortalezas de cientos de escuelas, combinadas con lo que dijo mi maestro en clase. , y concluyó las siguientes conclusiones:

  1. el ciclo de vida está rotoCreo que esta es la razón más importante., pero también es difícil de entender. ¿Cómo lo entiendes? ¿Recuerdas cómo describí por primera vez la primavera? ¿Cuál es el núcleo de Spring? ¿Sabe en qué parte del ciclo de vida del bean se encuentra la implementación de AOP?
    • La primera pregunta: Spring es un contenedor IOC que implementa tecnología AOP
    • La segunda pregunta: el núcleo de Spring es el COI y el AOP, pero todos los cimientos provienen del COI.
    • La tercera pregunta: AOP se implementa en la etapa [post-inicialización] del ciclo de vida del bean. porque,La implementación actual de la tecnología AOP también se basa en algunos de los muchos puntos de extensión proporcionados por Spring.. Por ejemplo, la implementación de AOP utiliza: BeanPostProcessor. ¿Cuál es el significado revelado aquí? Creo que significa:Dentro de Spring, AOP solo se usa como una extensión adicional.. Es como si implementáramos Mybatis basado en el punto de extensión de Spring y SpringMVC.

PD: Entonces, cuando lleguen aquí, ¿saben cómo entender esto [ el ciclo de vida está roto ] ? Si juzgamos si necesitamos hacer AOP después de la creación de instancias, significa que no hemos realizado [inyección de propiedad], [antes de la inicialización], [inicialización], [después de la inicialización] y otros ciclos de vida, debemos comenzar a hacerlo AOP, mueva directamente el proceso AOP de [después de la inicialización] a [antes de la inyección de atributos]. Y, en el proceso de implementación de este AOP, debes llamar a un método similar al siguiente:

for(BeanPostProcessor bp : this.beanPostProcessorsCache) {
     
     
		bp.postProcessAfterInitialization(bean);
}

Pero este código en realidad se llamará más tarde [después de la inicialización]. Supongo que algunos amigos dirán esto: ¿Entonces puedo recorrer los BeanPostProcessors especificados que implementan AOP? Bueno, realmente funciona. Sin embargo, como se mencionó anteriormente, si lo miramos desde la perspectiva de Spring: AOP es solo una extensión de mi COI. Desde este punto de vista, esta implementación es un poco intrusiva y la semántica también ha cambiado ligeramente.

  1. Las dependencias cíclicas ocurren con frecuencia. Creo que me gustaría preguntarles a todos: ¿tienen muchas dependencias circulares en sus escenarios de uso reales? Hermano, he estado escribiendo código Java durante más de 4 años y solo lo recuerdo unas pocas veces. Entonces, ¿qué tal si vuelves a mirar la solución anterior?Emite un juicio cada vez que crea una instancia y genera un bean.! ¿Es un poco redundante?
  2. estilo de código. Es muy abstracto decir esto, no es una afirmación muy convencional, pero tiene sentido. ¿Cómo entender esta frase?

De hecho, 2 y 3 deberían combinarse y basarse en la última lucha de [1], es decir, todavía tengo que comenzar a juzgar [si se necesita AOP] después de que se complete [la creación de instancias]. En primer lugar, ¿todavía recuerdas por qué debería juzgarse AOP de antemano? Por la necesidad de dependencias circulares. en otras palabras,En realidad necesitamos [juzgar si es AOP] cuando [hay dependencia circular], ¿verdad? Es decir, cuando [no hay dependencia circular], no hay necesidad alguna. Entonces, si juzgas directamente a AOP aquí, ¿la granularidad es demasiado grande?

3.2 Resuelva el problema de 3.1 [granularidad grande]

Creo que, en respuesta al problema de la "gran granularidad", esos estudiantes muy inteligentes ya han pensado en el diseño del "caché de tercer nivel" que les dije antes. Oye, lo diseñé como un caché de función de enlace. ¿El caché de segundo nivel compuesto por Spring [Nivel 1 + Nivel 3] también puede resolver el problema de la granularidad? (Esto es un poco difícil de entender, lea atentamente la expresión lambda), de la siguiente manera:
(PD: el mapa corresponde al caché de tercer nivel de Spring)

Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16)

singletonFactories.put(beanName, ()->{
    
    determinedAop(bean);});

void determinedAop(Object bean) {
    
    
	 Object exposedObject = object;
	 if (object 是否需要被代理) {
    
    
		 exposedObject = 代理对象obejct;
	 }
	 return exposedObject;
}

Sí, se parece un poco a eso. Sin embargo, todavía falta un juicio, es decir, juzgar [si existe una dependencia circular]. Esto también es fácil de manejar, simplemente juzgue si el mapa mencionado anteriormente tiene un valor o no. (PD: Creo que sí. Pero se agregó un nuevo conjunto en Spring para almacenar el nombre del bean que se está creando. Para los amigos interesados, lean mi último contenido). Pero, ¿es esto suficiente
? no es suficiente. ¿Por qué, qué pasa si tengo una tercera clase que requiere una dependencia circular? Como sigue:

@Component
public class CircularA {
    
    

    @Autowired
    CircularB b;
    
    @Autowired
    CircularC c;
}

@Component
public class CircularB {
    
    

    @Autowired
    CircularA a;
}

@Component
public class CircularC {
    
    

    @Autowired
    CircularA a;
}

El efecto es el siguiente:
inserte la descripción de la imagen aquí
es posible que no puedas verlo a través de la imagen, así que te lo recordaré directamente. Lo siguiente:
inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí
debido a que el mapa intermedio de arriba almacena una función de devolución de llamada, si hay un proxy para esta función, ¿devuelve un objeto nuevo cada vez? ? ? Obviamente, esto no está en línea con nuestras expectativas. En un solo caso, ¡A inyectado en B y C debería ser el mismo! ¿qué hacer? Sólo guárdalo. ¿Pero se puede poner directamente en el grupo singleton? No, ¿sabes por qué? Ja, la razón es la misma que la 1.1.

3.3 Resuelva el problema generado al llamar a la función de enlace varias veces en 3.2

Por lo tanto, aquí se presenta otro mapa de caché para almacenar en caché los beans mencionados anteriormente. Llámalo aquí: frijoles tempranos.(PD: el mapa corresponde al caché de segundo nivel de Spring)
Hasta ahora, se ha introducido el caché de tercer nivel. ¿Son los estudiantes inútiles?
inserte la descripción de la imagen aquí

3.4 Cómo juzgar si existe una dependencia circular (no tan importante)

Como se mencionó anteriormente, al juzgar si existe una dependencia circular, aunque creo que se puede usar el mapa de tercer nivel para juzgar, después de todo, esta existencia también representa [crear]. Pero Spring ha agregado un nuevo conjunto para almacenar el nombre del bean que se está creando. Por qué esto es así, creo que hay varias razones:

  1. En teoría, este mapa de caché de tercer nivel se eliminará inmediatamente después de su uso. ¿Cuáles son los beneficios de hacer esto? Hasta cierto punto, se reduce el riesgo de llamadas múltiples;
  2. Ahora se singletonsCurrentlyInCreationjuzga por un conjunto llamado Conjunto. Luego, los grandes pueden hacer clic para ver dónde se hace referencia a este juicio, y encontrarán que hay muchas referencias y que el ciclo de vida abarcado es más amplio. O, digámoslo de otra manera, muchos lugares necesitan juzgar si el bean se está [creando], por lo que se agrega un nuevo conjunto para guardarlo. El uso del mapa de tercer nivel para juzgar solo es aplicable a la escena de [dependencia circular]. Entonces, dado que ya existe un conjunto, úselo directamente y la semántica será más clara.

3.5 Declaración especial

Presta atención, lo que hice en 3.1, 3.2 y 3.3 es solo para mejorar el plan 2.2 ¿Aún recuerdas la conclusión de 2.2? [Tenemos que considerar AOP con anticipación en este paso], es decir, es inevitable, y el plan de mejora posterior es simplemente reducir la granularidad tanto como sea posible.

4. Resumen

Ah, me temo que no lo dejé claro. También me temo que no todos entendieron. Dé otro diagrama de evolución del razonamiento.
inserte la descripción de la imagen aquí

En segundo lugar, el análisis del código fuente subyacente (expansión)

La entrada del código fuente de la dependencia circular está justo aquí. AbstractAutowireCapableBeanFactory#doCreateBean()Al mirar el nombre, deberías quedar algo impresionado, ¿verdad? No pases también este método al crear instancias. De hecho, estrictamente hablando, el código fuente aquí no es nada de qué hablar, lo principal es comprender sus principios subyacentes. Permítame publicarlo para usted de manera informal, preste atención a los comentarios dentro, lo marcaré en el código relacionado con [dependencia circular]:

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {
    
    

		// 实例化bean
		BeanWrapper instanceWrapper = null;
		if (mbd.isSingleton()) {
    
    
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
		}
		if (instanceWrapper == null) {
    
    
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
		Object bean = instanceWrapper.getWrappedInstance();
		Class<?> beanType = instanceWrapper.getWrappedClass();
		if (beanType != NullBean.class) {
    
    
			mbd.resolvedTargetType = beanType;
		}
		
		// 合并beanDefinition Bean后置处理器
		synchronized (mbd.postProcessingLock) {
    
    
			if (!mbd.postProcessed) {
    
    
				try {
    
    
					applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
				}
				catch (Throwable ex) {
    
    
					throw new BeanCreationException(mbd.getResourceDescription(), beanName,
							"Post-processing of merged bean definition failed", ex);
				}
				mbd.postProcessed = true;
			}
		}

		// 【循环依赖】关键源码一
		// 这里就是我们在分析中说的注册钩子方法,判断是否需要【循环依赖】
		boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
				isSingletonCurrentlyInCreation(beanName));
		if (earlySingletonExposure) {
    
    
			if (logger.isTraceEnabled()) {
    
    
				logger.trace("Eagerly caching bean '" + beanName +
						"' to allow for resolving potential circular references");
			}
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}

		// 【循环依赖】关键源码二
		// 但是这里看不出来,因为AOP就是在下面的initializeBean里面的,需要挖掘一下才能找到
		Object exposedObject = bean;
		try {
    
    
			populateBean(beanName, mbd, instanceWrapper);
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}
		catch (Throwable ex) {
    
    
			if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
    
    
				throw (BeanCreationException) ex;
			}
			else {
    
    
				throw new BeanCreationException(
						mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
			}
		}

		// 【循环依赖】关键源码三
		if (earlySingletonExposure) {
    
    
			Object earlySingletonReference = getSingleton(beanName, false);
			if (earlySingletonReference != null) {
    
    
				if (exposedObject == bean) {
    
    
					exposedObject = earlySingletonReference;
				}
				else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
    
    
					String[] dependentBeans = getDependentBeans(beanName);
					Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
					for (String dependentBean : dependentBeans) {
    
    
						if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
    
    
							actualDependentBeans.add(dependentBean);
						}
					}
					if (!actualDependentBeans.isEmpty()) {
    
    
						throw new BeanCurrentlyInCreationException(beanName,
								"Bean with name '" + beanName + "' has been injected into other beans [" +
								StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
								"] in its raw version as part of a circular reference, but has eventually been " +
								"wrapped. This means that said other beans do not use the final version of the " +
								"bean. This is often the result of over-eager type matching - consider using " +
								"'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
					}
				}
			}
		}

		// Register bean as disposable.
		try {
    
    
			registerDisposableBeanIfNecessary(beanName, bean, mbd);
		}
		catch (BeanDefinitionValidationException ex) {
    
    
			throw new BeanCreationException(
					mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
		}

		return exposedObject;
	}

Como se muestra arriba, hay 3 códigos fuente críticos arriba.

2.1 El primer código fuente clave

El primer código fuente clave aquí es el siguiente:

// 【循环依赖】关键源码一
// 这里就是我们在分析中说的注册钩子方法,判断是否需要【循环依赖】
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
		isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
    
    
	if (logger.isTraceEnabled()) {
    
    
		logger.trace("Eagerly caching bean '" + beanName +
				"' to allow for resolving potential circular references");
	}
	addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}

allowCircularReferences Determine si [permite la dependencia circular] y isSingletonCurrentlyInCreation(beanName)determine si [existe la dependencia circular]. Luego llame addSingletonFactoryal método de enlace registrado getEarlyBeanReference(beanName, mbd, bean). El método de gancho se implementa de la siguiente manera:

	protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    
    
		Object exposedObject = bean;
		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
    
    
			for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
    
    
				exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
			}
		}
		return exposedObject;
	}

Mire, hay un [postprocesador Bean inteligente que reconoce la creación de instancias SmartInstantiationAwareBeanPostProcessor ]. Y llamamos a su getEarlyBeanReferencemétodo para obtener una referencia a un objeto [early bean]. Oye, este [early bean] es muy familiar tan pronto como sale, el caché de segundo nivel, ¿no tiene nada de malo?
Desde este punto, no es difícil ver que este postprocesador Bean tiene una gran relación con la implementación AOP de Spring. Pero quiero decirles de antemano que la clase principal de AOP es en realidad una clase de implementación de esta interfaz. AnnotationAwareAspectJAutoProxyCreatorEn cuanto a cómo profundizar, presentaré el curso de AOP más adelante. Pero al final profundizamos en getEarlyBeanReferenceeste método, y finalmente su código fuente es el siguiente: (PD: cuando llamamos al siguiente método, significa que el juicio de AOP se realiza de antemano

public Object getEarlyBeanReference(Object bean, String beanName) {
    
    
	Object cacheKey = getCacheKey(bean.getClass(), beanName);
	this.earlyProxyReferences.put(cacheKey, bean);
	return wrapIfNecessary(bean, beanName, cacheKey);
}

Este código fuente es muy simple, básicamente puedes saber lo que significa mirando el nombre. Al final, return wrapIfNecessaryno es más que el objeto original o el objeto proxy, dependiendo de [si se requiere AOP].
El grandullón puede preguntarse, ¿ this.earlyProxyReferences.put(cacheKey, bean);para qué sirve la segunda línea de caché? Oye, el AOP normal es [después de la inicialización], ahora estás adelantado a lo previsto, ¿por qué no lo grabas? Si no lo graba, ¿desea realizar AOP nuevamente cuando vaya a [después de la inicialización]? Oye, eso es todo.

2.2 El segundo código fuente clave

El segundo código fuente clave dice esto:

// 【循环依赖】关键源码二
// 但是这里看不出来,因为AOP就是在下面的initializeBean里面的,需要挖掘一下才能找到
Object exposedObject = bean;
try {
    
    
	populateBean(beanName, mbd, instanceWrapper);
	exposedObject = initializeBean(beanName, exposedObject, mbd);
}

Pero, de hecho, quiero hablar sobre initializeBeanel procesamiento interno [post-inicialización]. Supongo que los estudiantes experimentados, o los estudiantes que han visto mi introducción a Spring antes, deberían saber qué método [después de la inicialización] es. No lo indexaré, solo mostraré el código fuente de llamada:

	@Override
	public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
			throws BeansException {
    
    

		Object result = existingBean;
		for (BeanPostProcessor processor : getBeanPostProcessors()) {
    
    
			Object current = processor.postProcessAfterInitialization(result, beanName);
			if (current == null) {
    
    
				return result;
			}
			result = current;
		}
		return result;
	}

El lugar de llamada es así, pero el código fuente del postprocesador Bean que maneja AOP es el siguiente, no lo indexaré en el medio, aquí hay solo una impresión para que todos la vean:

	@Override
	public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    
    
		if (bean != null) {
    
    
			Object cacheKey = getCacheKey(bean.getClass(), beanName);
			if (this.earlyProxyReferences.remove(cacheKey) != bean) {
    
    
				return wrapIfNecessary(bean, beanName, cacheKey);
			}
		}
		return bean;
	}

Mire, aquí está la utilidad de earlyProxyReferencesjuzgar si AOP se ha realizado de antemano. ¡Pero aquí hay un detalle muy importante, que se utilizará cuando hablemos del tercer código fuente clave a continuación!
Ahora mismo:Cuando this.earlyProxyReferences.remove(cacheKey) == beanes así, significa que he realizado AOP por adelantado, y en este momento el objeto original se devuelve directamente, ¡no el objeto proxy! (Lógica normal, cuando no hay AOP por adelantado, el objeto proxy se devuelve aquí). Teniendo presente esta conclusión, examinaremos

2.3 El tercer código fuente clave

como sigue:(Nota: Existe un requisito previo clave para ingresar este código, a saber: earlySingletonExposure==true, lo que significa que antes había una dependencia circular.

// 【循环依赖】关键源码三
if (earlySingletonExposure) {
    
    
	Object earlySingletonReference = getSingleton(beanName, false);
	if (earlySingletonReference != null) {
    
    
		if (exposedObject == bean) {
    
    
			exposedObject = earlySingletonReference;
		}
		else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
    
    
			String[] dependentBeans = getDependentBeans(beanName);
			Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
			for (String dependentBean : dependentBeans) {
    
    
				if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
    
    
					actualDependentBeans.add(dependentBean);
				}
			}
			if (!actualDependentBeans.isEmpty()) {
    
    
				throw new BeanCurrentlyInCreationException(beanName,
						"Bean with name '" + beanName + "' has been injected into other beans [" +
						StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
						"] in its raw version as part of a circular reference, but has eventually been " +
						"wrapped. This means that said other beans do not use the final version of the " +
						"bean. This is often the result of over-eager type matching - consider using " +
						"'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
			}
		}
	}
}

Para ser honesto, es bastante problemático hablar de esto, y este conocimiento es un poco impopular y es difícil de describir, se estima que no se encuentra con frecuencia. Sin embargo, podemos saber aproximadamente cuál es la lógica a procesar aquí a través de la información de excepción arrojada al final. La traducción es la siguiente:

Traducción literal de excepción: el Bean con el nombre "beanName" se inyectó en otros Beans [xxxx] en su versión anterior como parte de una referencia circular, pero finalmente se envolvió (envuelto significa: proxy). Esto significa que otros beans mencionados anteriormente utilizan una versión no final del bean.
. . . . . . . . . . . . . . . . . . . .
En palabras humanas: el Bean llamado beanName ya ha sido empaquetado por [proxy AOP] una vez en la dependencia circular, pero fue empaquetado nuevamente por [paquete proxy] más adelante en otro procesamiento. Como resultado, el bean proxy que se inyectó previamente en [Dependencia circular] no es el bean proxy más reciente. Si te encuentras con esta situación, solo puedes informar un error.

Mire, a través de la traducción anterior, queda muy claro lo que está pasando. Esa es la cuestión, ese es el problema.

Pero, de hecho, todavía hay un problema con el código fuente de Spring. Debido a la existencia de la conclusión en 2.2, puede conducir a la situación mencionada en el cuadro de juicio negro en la siguiente figura, y luego se informa un error: inserte la descripción de la imagen aquí
el proceso se debe a que, en el proceso de instanciación de A, hay una circular dependencia entre A y B, y el agente AOP de A. El objeto se genera utilizando el objeto del método de enlace en el proceso de B, por lo que, en el proceso de creación de A, A no puede percibir que se ha generado como un proxy AOP. Entonces, en la etapa posterior, cuando llegamos al proceso de [después de la inicialización], debido a la conclusión de 2.2, el objeto original fue utilizado y Async lo representó. ¡horrible! ¡Obviamente esto no es lo que queremos!

2.3.1 Cómo resolver los problemas anteriores

De hecho, la idea es muy sencilla: dado que este problema es causado por la dependencia circular, ¡rompamos la dependencia circular! Espera, espera, ¿no crees que te dije que cambiaras la estructura de clases? No. Entonces piénselo, ¿hay alguna forma de romper la dependencia circular sin cambiar la estructura de clases?
Hablando de eso, recordemos, ¿todavía recuerdas qué causó la dependencia circular? ¿No es por los atributos que se necesita [inyección en serie]? La explicación popular es que necesito inyectar propiedades en el proceso de creación de beans. ¿Hay alguna manera de no inyectar propiedades al crear beans? Hay, @Lazycomenta.

resumir

  1. Conozca las causas de las dependencias circulares y los principios de diseño de la estructura de caché de tres niveles.

Supongo que te gusta

Origin blog.csdn.net/qq_32681589/article/details/132416307
Recomendado
Clasificación