"Spring AOP encounter cycle dependent" Spring Source finale final exam questions, not many people really understand!

Foreword

Q: How to solve the Spring 循环依赖?
A: Spring through 提前曝光mechanism, utilize 三级缓存to solve 循环依赖(this principle is very simple, refer to: level three cache , graphic circular dependencies principle )
to ask: Spring through 提前曝光direct exposure to the 二级缓存already solved the problem of circular dependencies, why must 三级缓存?
Then fine Q: If the cycle of dependence when all the classes they need Spring AOP自动代理, that Spring how early exposure? Exposure is 原始beanstill 代理后的bean?

These questions regarded the finale theme Spring source, if these issues are to figure out, congratulations successful completion of the Spring source. Spring alone on this one for understanding, not an exaggeration to say that Ali can reach the level

Source code analysis

Entered, in the Spring 创建Beancore code doGetBean, the 实例化beanbefore, we will first try from 三级缓存obtaining bean, which is the beginning of Spring to resolve circular dependencies

(A) obtaining bean cache

// AbstractBeanFactory.java
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
			@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

		final String beanName = transformedBeanName(name);
		Object bean;

		// 2. 尝试从缓存中获取bean
		Object sharedInstance = getSingleton(beanName);
		...
}
	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		// 从一级缓存获取,key=beanName value=bean
		Object singletonObject = this.singletonObjects.get(beanName);
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			synchronized (this.singletonObjects) {
				// 从二级缓存获取,key=beanName value=bean
				singletonObject = this.earlySingletonObjects.get(beanName);
				// 是否允许循环引用
				if (singletonObject == null && allowEarlyReference) {
					/**
					 * 三级缓存获取,key=beanName value=objectFactory,objectFactory中存储getObject()方法用于获取提前曝光的实例
					 *
					 * 而为什么不直接将实例缓存到二级缓存,而要多此一举将实例先封装到objectFactory中?
					 * 主要关键点在getObject()方法并非直接返回实例,而是对实例又使用
					 * SmartInstantiationAwareBeanPostProcessor的getEarlyBeanReference方法对bean进行处理
					 *
					 * 也就是说,当spring中存在该后置处理器,所有的单例bean在实例化后都会被进行提前曝光到三级缓存中,
					 * 但是并不是所有的bean都存在循环依赖,也就是三级缓存到二级缓存的步骤不一定都会被执行,有可能曝光后直接创建完成,没被提前引用过,
					 * 就直接被加入到一级缓存中。因此可以确保只有提前曝光且被引用的bean才会进行该后置处理
 					 */
					ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
					if (singletonFactory != null) {
						/**
						 * 通过getObject()方法获取bean,通过此方法获取到的实例不单单是提前曝光出来的实例,
						 * 它还经过了SmartInstantiationAwareBeanPostProcessor的getEarlyBeanReference方法处理过。
						 * 这也正是三级缓存存在的意义,可以通过重写该后置处理器对提前曝光的实例,在被提前引用时进行一些操作
 						 */
						singletonObject = singletonFactory.getObject();
						// 将三级缓存生产的bean放入二级缓存中
						this.earlySingletonObjects.put(beanName, singletonObject);
						// 删除三级缓存
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		return singletonObject;
	}

Three-level cache are:

  • singletonObject: Cache, the cache key = beanName, value = bean;where the bean is already created, which experienced a bean instantiated -> Properties padding -> initialization and post processing of all kinds. Therefore, once the need to get the bean, the first time we will look for a cache
  • earlySingletonObjects: Secondary cache, the cache key = beanName, value = bean;here and tell the difference between a cache that the cache is acquired early exposure to the bean out, is not created in. That is to get the bean can only have been carried out to ensure that instantiated, but the property is filled with initialization certainly not finished, so the bean has not been created, only can be used as a pointer early exposure, was quoted other bean
  • singletonFactories: Three cache that key = beanName, value = beanFactory;after completion of the bean instance, before the filling property and initialization, if allowed to early exposure, the bean will be instantiated spring early exposure, i.e. converted into the bean beanFactoryand added to a three-level cache. And through exposure in advance when you need to reference the object singletonFactory.getObject()to obtain.

Here thrown issue, if we advance directly to the exposure of an object into the secondary cache earlySingletonObjects, the Spring you can take directly to resolve circular dependencies when the cycle of dependence, why three-level cache singletonFactoryand then through getObject()to get it? It is not superfluous?


(B) adding three cache

We add back three cache, add SingletonFactoryplaces to see getObject()in the end what has been done operation

	this.addSingletonFactory(beanName, () -> {
		return this.getEarlyBeanReference(beanName, mbd, bean);
	});

Can be seen at the return getObject()time, do step getEarlyBeanReferenceoperation, this operation is BeanPostProcessone, i.e. to a subclass overrides the post-processor for purpose 被提前引用to expand upon. Namely: the exposure time is not calling the post-processor, only exposure, and when they were cited only call in advance to ensure that 被提前引用this opportunity is triggered.


(C) early exposure proxy earlyProxyReferences

So all the emphasis fell getEarlyBeanReferenceon the getEarlyBeanReferenceway is SmartInstantiationAwareBeanPostProcessorthe interface specified. View implementation class through UML class diagram, just AbstractAutoProxyCreatorwere achieved. In other words, in addition to the user subclass overrides, otherwise only AbstractAutoProxyCreatorone case

	// AbstractAutoProxyCreator.java
	public Object getEarlyBeanReference(Object bean, String beanName) {
		// 缓存当前bean,表示该bean被提前代理了
		Object cacheKey = getCacheKey(bean.getClass(), beanName);
		this.earlyProxyReferences.put(cacheKey, bean);
		// 对bean进行提前Spring AOP代理
		return wrapIfNecessary(bean, beanName, cacheKey);
	}

wrapIfNecessarySpring AOP is used for automatic proxy. Spring bean cache to the current earlyProxyReferencesidentified 提前曝光的beanbefore being referenced in advance, and then the Spring AOP proxy.

But after the Spring AOP proxy bean after it is no longer the original bean, and the bean through a proxy is a new bean, that is to say two bean even before and after the memory address of the agent are not the same. Then Jiangzai lead to new problems: B A reference to earlier references to the agent A, which is common sense, but most of the original bean A complete B will continue to create the creation, then returned last Spring Ioc Bean is Bean a proxy or do after Bean it?

This is something we have to return to Spring AOP proxy, Spring AOP proxy timing has two:

  1. When a custom TargetSource, is completed before Spring AOP proxy bean instantiated and short-circuit operation occur directly returns bean
  2. Under normal circumstances, are carried out after the Spring AOP proxy bean initialization
  3. If you want to add said today that early exposure agent, getEarlyBeanReferencespoke three

The first case have nothing to explore, and a direct short circuit, there is no follow-up action. And we care about is the second case, Spring AOP proxy in Spring initialization occurred in the post processor

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

		Object result = existingBean;
		for (BeanPostProcessor processor : getBeanPostProcessors()) {
			// 调用bean初始化后置处理器处理
			Object current = processor.postProcessAfterInitialization(result, beanName);
			if (current == null) {
				return result;
			}
			result = current;
		}
		return result;
	}
	// AbstractAutoProxyCreator.java
	public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
		if (bean != null) {
			// 获取缓存key
			Object cacheKey = getCacheKey(bean.getClass(), beanName);
			// 查看该bean是否被Spring AOP提前代理!而缓存的是原始的bean,因此如果bean被提前代理过,这此处会跳过
			// 如果bean没有被提前代理过,则进入AOP代理
			if (this.earlyProxyReferences.remove(cacheKey) != bean) {
				return wrapIfNecessary(bean, beanName, cacheKey);
			}
		}
		return bean;
	}

earlyProxyReferencesIs not it a bit familiar, yes, this is what we have just ahead of the exposure and cache when the original bean Spring AOP proxy in advance, if the cache with the current raw bean is a bean to, then do not be a Spring AOP proxy! Return the original bean

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
			throws BeanCreationException {
		try {
			//
			/**
			 * 4. 填充属性
			 * 如果@Autowired注解属性,则在上方完成解析后,在这里完成注入
			 *
			 * @Autowired
			 * private Inner inner;
			 */
			populateBean(beanName, mbd, instanceWrapper);
			// 5. 初始化
			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);
			}
		}

		// 6. 存在提前曝光情况下
		if (earlySingletonExposure) {
			// earlySingletonReference:二级缓存,缓存的是经过提前曝光提前Spring AOP代理的bean
			Object earlySingletonReference = getSingleton(beanName, false);
			if (earlySingletonReference != null) {
				// exposedObject跟bean一样,说明初始化操作没用应用Initialization后置处理器(指AOP操作)改变exposedObject
				// 主要是因为exposedObject如果提前代理过,就会跳过Spring AOP代理,所以exposedObject没被改变,也就等于bean了
				if (exposedObject == bean) {
					// 将二级缓存中的提前AOP代理的bean赋值给exposedObject,并返回
					exposedObject = earlySingletonReference;
				}
				// 引用都不相等了,也就是现在的bean已经不是当时提前曝光的bean了
				else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
					// dependentBeans也就是B, C, D
					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 " +
								"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
					}
				}
			}
		}

This time we need to sort out what three variables

  1. earlySingletonReference: Secondary cache, the cache is the result of early exposure in advance AOP proxy bean
  2. bean: This is after instantiation, filling the bean initialization
  3. exposedObject: This is the result of AbstractAutoProxyCreatorthe postProcessAfterInitializationbean after the treatment, but because of where the bean has been found in the current earlyProxyReferencescache, and there is no AOP process, but skip, so it is like the first 2:00 of the bean

After clarifying these three variables, you will find, exposedObject = earlySingletonReference;
AOP proxy had assigned to the Bean exposedObjectand return, this time the user get after the AOP proxy bean is a bean, all the satisfaction of all.

But in the middle there is a problem! Early exposure when the bean is referenced early Spring AOP proxy, but this time just after the bean instantiated bean, has not been injected @Autowire, ah! That at this time the agent automatically injected bean inside the property is empty!

(D) filling property in advance AOP proxy object, initialized

Yes, indeed Spring AOP提前代理not post 属性填充and 初始化. So this is how to ensure that the proxy dependency injection property it? Spring AOP answer back to the earliest of the earliest talking about JDK dynamic proxy on to find, when JDK dynamic proxies, will be 目标对象targetsaved at the last generation agent $proxy, when invoked $proxywhen the callback method will be h.invoke, and h.invokewill callback 目标对象targetprimitive methods. So really, when Spring AOP dynamic agency, 原始beanhas been saved in the 提前曝光代理in the. Then 原始Beancontinue to complete 属性填充and 初始化operate. Because AOP proxy $proxyin holds tragetis a 原始beanreference, subsequent 原始beanimprovement, it is equivalent to Spring AOP is targetperfect, thus ensuring the Spring AOP 属性填充and 初始化up!

(E) illustrates a circular dependency met Spring AOP

To help you understand where the soul of painting hand draw a flow chart to help you understand
first and bean A, bean B, their cycle of dependency injection, but also need to be bean A Spring AOP proxy, such as transaction management or operation logs and the like.
Original bean A, bean B figure by a, b, said the agency bean A while after we aop.a express
Spring AOP circular dependencies
watching can be considered successful completion of the Spring!

Paint is not easy to understand the small partners give praise, what is wrong place, or do not understand written comments welcome exchange!
Here Insert Picture Description

Published 42 original articles · won praise 43 · views 40000 +

Guess you like

Origin blog.csdn.net/chaitoudaren/article/details/105060882