什么是循环依赖
循环依赖其实就是循环引用,两个或两个以上的Bean相互依赖,相互引用,例如A依赖B,B依赖于A,如果不加以处理,Bean A,Bean B 都无法创建成功。因为创建A的时候发现依赖B,于是去创建B,创建B的时候依赖A,这样整个SpringBean创建周期无法完成。
Spring中循环依赖的场景
依赖注入方式
- 构造器注入导致循环依赖
- set注入
- 注解注入
构造器注入导致的循环依赖,无法被解决,因为创建A需要对象B,B又需要A,连基本的反射创建对象都创建不了。实际上就是Bean B ,Bean A 实例化不了。只能拋出 BeanCurrentlyInCreationException 异常
refresh方法
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 刷新前预处理
prepareRefresh();
// 获取之前创建的BeanFactory,默认实现DefaultListableBeanFactory
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// BeanFactory的预准备⼯作(BeanFactory进⾏⼀些设置,设置类加载器,环境等等。
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// 实例化并调用默认配置,和自定义的BeanFactoryPostProcessor,实现核心配置类解析,包组件扫描封装BeanDefinition.
invokeBeanFactoryPostProcessors(beanFactory);
// 注册默认配置的和自定义的BeanPostProcessor , 实现bean创建赋值之后 init 方法前后调用
registerBeanPostProcessors(beanFactory);
// 初始化MessageSource组件,实现 国际化功能,消息绑定,消息解析
initMessageSource();
// 初始化应用事件派发器
initApplicationEventMulticaster();
// 子类重写
onRefresh();
// 注册实现了ApplicationListener接⼝的监听器
registerListeners();
// 实例化剩下的所有单例bean
finishBeanFactoryInitialization(beanFactory);
// 发布事件,交由其它组件触发对应响应,例如springMVC初始化
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
循环依赖源码剖析
定义两个循环依赖的Bean A,B
测试类
1、由于我们之前分析过SpringBean的创建流程,因此直接定位到 实例化当前BeanDefinitionMap中的所有单例BeanDefintion
可以看到A,B已经被封装成BeanDefinition
注意:finishBeanFactoryInitialization(beanFactory);
2、目前正要实例化Bean A
默认通过无参构造反射实例化当前Bean A
获取无参构造器,获取实例化对象
3、主动的缓存单例Bean A,以便解决循环依赖问题,默认放入三级缓存池SingletonFactorys,注意当前Bean A 只是一个空对象
4、对当前Bean A进行属性赋值
5、获取默认,和自定义的BeanPostProcessor,找到AutowireAnnotationBeanPostProcessor,实现依赖注入元素处理,当前Bean A,依赖于Bean B,那么通过他去实例化Bean B 实现依赖注入
6、实例化Bean B,将Bean B也放入三级缓存singletonFactorys;
7、同样对Bean B实现依赖注入,通过AutowiredAnnotationBeanPostProcessor
8、从IOC容器中,获取Bean B 所依赖的元素Bean A,获取位置应为三级缓存SingletonFactorys,因为singletonObjects是没有A实例的,一旦从三级缓存中获取了Bean A那么,Bean A 就会剪切到二级缓存(重点!!!!)
9、Bean B 实现依赖注入 A之后,继续完成SpringBean的创建周期,例如 调用XXXAware对应的set方法 ,调用 Bean B
init 方法,在init方法前后调用BeanPostProcessor。最终Bean B 成为了SpringBean B,放入了一级缓存SingletonObejcts中,移除二三级缓存中的Bean B
10、此时轮到Bean A 继续进行Bean属性赋值,既然一级缓存池中,已经存在了SpringBean B,那么将其注入到Bean A 当中完成依赖注入
11、Bean A 依赖注入后,初始化后(注意:经过后置处理器的Bean 可能是代理对象)。将二级缓存池中的Bean A取出,此时两个Bean A 引用默认指向同一个对象
// earlySingletonExposure:如果你的bean允许被早期暴露出去 也就是说可以被循环引用 那这里就会进行检查
// 此段代码非常重要~~~~~但大多数人都忽略了它
if (earlySingletonExposure) {
// 此时一级缓存肯定还没数据,但是呢此时候二级缓存earlySingletonObjects也没数据
//注意,注意:第二参数为false 表示不会再去三级缓存里查了~~~
// 此处非常巧妙的一点:::因为上面各式各样的实例化、初始化的后置处理器都执行了,如果你在上面执行了这一句
// ((ConfigurableListableBeanFactory)this.beanFactory).registerSingleton(beanName, bean);
// 那么此处得到的earlySingletonReference 的引用最终会是你手动放进去的Bean最终返回,完美的实现了"偷天换日" 特别适合中间件的设计
// 我们知道,执行完此doCreateBean后执行addSingleton() 其实就是把自己再添加一次 **再一次强调,完美实现偷天换日**
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
// 这个意思是如果经过了initializeBean()后,exposedObject还是木有变,那就可以大胆放心的返回了
// initializeBean会调用后置处理器,这个时候可以生成一个代理对象,那这个时候它哥俩就不会相等了 走else去判断吧
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
// allowRawInjectionDespiteWrapping这个值默认是false
// hasDependentBean:若它有依赖的bean 那就需要继续校验了~~~(若没有依赖的 就放过它~)
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
// 拿到它所依赖的Bean们~~~~ 下面会遍历一个一个的去看~~
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
// 一个个检查它所以Bean
// removeSingletonIfCreatedForTypeCheckOnly这个放见下面 在AbstractBeanFactory里面
// 简单的说,它如果判断到该dependentBean并没有在创建中的了的情况下,那就把它从所有缓存中移除~~~ 并且返回true
// 否则(比如确实在创建中) 那就返回false 进入我们的if里面~ 表示所谓的真正依赖
//(解释:就是真的需要依赖它先实例化,才能实例化自己的依赖)
for (String dependentBean : dependentBeans) {
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
// 若存在真正依赖,那就报错(不要等到内存移除你才报错,那是非常不友好的)
// 这个异常是BeanCurrentlyInCreationException,报错日志也稍微留意一下,方便定位错误~~~~
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.");
}
}
}
}
12、Bean A 完成创建流程,将其放入一级缓存池SingletonObjects中,移除二级,三级缓存池中的Bean,由此Bean A,转换为了SpringBean A。
最终结果:发现通过注解没有出现循环依赖Bean不能创建的问题
总结:
注意:依赖注入交由Bean属性赋值阶段的AutowireAnnotationBeanPostProcessor处理。
在Bean初始化阶段的时候通过BeanPostProcessor实现动态代理对对象进行代理。如果是循环依赖对象,那么该 代理对象此时,应该在二级缓存earlySingletonObjects中
SingletonObejcts: 一级缓存,存放完整的SpringBean,代理Bean,Bean最后的归宿
EarlySingletonObjects: 二级缓存,存放代理Bean, 普通Bean
SingletonFactorys: 三级缓存,存放原始Bean,即反射调用构造方法创建的Bean