bean
bean
实例化bean
依赖注入bean
创建,注入单例池bean
销毁循环依赖产生处于依赖注入这阶段,例:BeanA
和BeanB
直接存在循环依赖:
BeanA
,给BeanA
进行依赖注入BeanA
中存在BeanB
,单例池中不存在BeanB
,需要创建BeanB
BeanB
实例化后,给BeanB
进行依赖注入BeanB
中存在BeanA
,单例池中不存在BeanA
,需要创建BeanA
由上逻辑可以发现当 BeanA
注入BeanB
时,BeanB
需要注入BeanA
时由于BeanA
已经被实例化了(虽然属性不完整),那么就可以给BeanB
先注入这个早期的BeanA
,那么BeanB
就可以继续后续环节,完成后BeanA
也能继续后续环节,这时的BeanA
就是一个完善的 bean
了,由于这个bean
和BeanB
中的是同一个对象,那么BeanB
中的BeanA
也是一个完整的bean
,这样就解决了循环依赖的问题。
经过上面分析,可以看出我们需要一个能得到 bean
实例化后的早期对象的地方,而 Spring
中就提供了 Map
来存储早期 bean
。
经过上面分析似乎解决了循环依赖问题,但是当BeanA
需要 AOP
生成代理对象时就会出现问题:
BeanB
中存储的早期BeanA
是原始对象,那么到最后就会出现BeanB
中的BeanA
是一个原始对象而不是代理对象问题。
为了解决上面问题,可以发现:如果能根据这个bean
是否出现循环依赖而提前对bean
进行AOP
从而生成其代理对象存储,之后也BeanB
中也直接利用这个代理对象BeanA
就好了。
抛出问题:什么时候能最方便判断这个
bean
是否被循环依赖了呢?
分析可以得到当在创建BeanB
,给BeanB
进行依赖注入时是可以很方便判断的,而Spring
也正提供了一个Set
来存储当前正在创建中的bean
,通过这个就可以在给BeanB
注入BeanA
时从这个Set
中判断BeanA
是否存在而知道是否出现了循环依赖。然后就可以判断是否需要进行AOP
生成代理对象,为BeanB
注入对应的代理对象/原始对象。
根据上面可以发现,无论是否需要生成代理对象,都需要用到原始对象(代理对象是基于原始对象生成的,而不需要生成代理对象也需要保证原始对象的单例),所以这里我们需要能根据注入的beanName
获取到原始对象,那么可以利用一个 Map
。
Spring
提供了一个 Map
达到上述功能。Map
的key
是beanName
,value
是ObjectFactory(一个函数式接口)
;Spring
传入的value
一个 Lambda
表达式,逻辑是判断该bean
是否需要进行AOP
需要则创建,不需要返回原始对象。
这样做的话就可以在实例化bean
后就存入这个Map
中,在属性注入时从这个Map
中取出value
执行Lambda
方法得到对应的对象。
// 实例化后置处理RootBeanDefinition后
// 是否单例 && 是否允许循环依赖 && 该bean是否处于创建中
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
,那么是否会在初始化后重复进行AOP
?
点进存入三级缓存的Lambda
表达式方法:
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;
}
可以看到处理的方法是 SmartInstantiationAwareBeanPostProcessor.getEarlyBeanReference()
public interface SmartInstantiationAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessor {
@Nullable
default Class<?> predictBeanType(Class<?> beanClass, String beanName) throws BeansException {
return null;
}
@Nullable
default Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName)
throws BeansException {
return null;
}
default Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
return bean;
}
}
而这个接口的 getEarlyBeanReference()
方法只有一个实现类 AbstractAutoProxyCreator
// 重点属性:存储早期代理对象;key:beanName/Class,value:代理对象
private final Map<Object, Object> earlyProxyReferences = new ConcurrentHashMap<>(16);
// 两个主要的方法
/**
* 初始化后方法(正常执行AOP的方法)
*/
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
// 早期AOP集合中的bean != 传入bean(即未被代理),并移除
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
/**
* 提前进行AOP调用方法
*/
@Override
public Object getEarlyBeanReference(Object bean, String beanName) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
// 将这个bean缓存到早期AOP集合
this.earlyProxyReferences.put(cacheKey, bean);
return wrapIfNecessary(bean, beanName, cacheKey);
}
看到上面可以得知是如何避免二次AOP
的,顺便看下创建代理的方法wrapIfNecessary
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
// advisedBeans表示已经判断过了的bean,false表示此bean不需要进行Aop
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
// 当前正在创建的Bean不用进行AOP,比如切面Bean
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// Create proxy if we have advice.
// 判断当前bean是否存在匹配的advice,如果存在则要生成一个代理对象
// 此处根据类以及类中的方法去匹配到Interceptor(也就是Advice),然后生成代理对象,代理对象在执行的时候,还会根据当前执行的方法去匹配
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
// advisedBeans记录了某个Bean已经进行过AOP了
this.advisedBeans.put(cacheKey, Boolean.TRUE);
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
在bean
实例化后置处理RootBeanDefinition
后,Spring
会将信息存入三级缓存Map
// 实例化后置处理RootBeanDefinition后
// 是否单例 && 是否允许循环依赖 && 该bean是否处于创建中
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));
}
进行依赖注入
对于bean
属性进行getBean()
,走到doGetBean()
–>getSingleton()
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 去单例池获取 bean
Object singletonObject = this.singletonObjects.get(beanName);
// 单例池不存在 && 这个bean正在创建中
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 去二级缓存(早期bean单例池)取
singletonObject = this.earlySingletonObjects.get(beanName);
// 不存在 && 允许循环依赖
if (singletonObject == null && allowEarlyReference) {
// 加锁考虑的是多线程的问题
// todo:在5.3.10之前是单例池未获取到对象就加锁,这里为了降低锁的粒度进行了改变
// 但是引发一个问题,就是当在将三级缓存生成的对象放入二级缓存后,此时失去CPU调度,另一个线程进入这个方法的开头会判断二级缓存存在直接返回
// 然而此时的bean并未完全创建成功导致获取的对象是个早期对象就会出现后进入的线程得到了对象但是属性值没注入成功
synchronized (this.singletonObjects) {
// 再次去单例池获取(在这里可能有其他线程的变动使对象被加入)
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
// 单例池和早期单例池都不存在
if (singletonObject == null) {
// 去三级缓存查找,Map
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// 三级缓存存在,则调用判断方法生成对应的对象(可能是代理/原始对象)
singletonObject = singletonFactory.getObject();
// 将生成的对象存入到二级缓存(早期单例池)
this.earlySingletonObjects.put(beanName, singletonObject);
// 从三级缓存移除
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
发生循环依赖注入早期对象(代理/原始),并将第三级缓存中的对象移除,进行后续操作完成bean
创建
完成外层对象的创建,将第二级缓存对象移除,第一级缓存添加完整对象
singletonObjects
bean
对象earlySingletonObjects
bean(代理/原始)
对象,是Bean
的生命周期未完整经历的对象singletonFactories
ObjectFactory(函数式接口)
,表示对象工厂,用来创建早期bean
对象的工厂AOP
需要用到原始对象,且保证原始对象单例问题AOP
判断缓存 earlyProxyReferences
AOP
的如果不用第三极缓存也是可以达到相同的结果,例:
BeanB
注入BeanA
时可以在后续方法中都增加传参原始对象bean
来解决问题AOP
逻辑进行修改AOP
生成代理对象并缓存到二级缓存earlySingletonObjects
中Spring Bean
生命周期的流程,也让代码显得臃肿对于多例Bean
每次获取都是要进行创建bean
的,是不存在其实例被缓存情况。那么如果多例bean
之间存在循环依赖就会出现反复创建的死循环,所以多例bean
的循环依赖Spring
并不能解决。
构造器注入是在实例化前就需要用到构造器参数,但是由于bean
还未实例化,所以也无法缓存得到早期对象,且Spring
建议使用构造器注入,并认为出现循环依赖是不规范的,构造器注入能及时发现问题。
不过如果一定要解决,可以配合使用 @Lazy
注解在指定构造器上,这样表明构造器需要的参数是懒加载的,就会先创建参数的代理对象供构造器使用。
@Async
的实现是利用了 BeanPostProcesser
。一个bean
中使用了@Async
,则会在初始化后阶段多执行一个用来处理@Async
的BeanPostProcesser
;而这个实现也是生成一个代理对象,那么就会出现循环依赖中两个bean
对象的不一致性(依赖注入的是通过三级缓存生成的代理对象,自己本身会多执行处理@Async
的BeanPostProcesser
生成新的代理),从而抛出异常。