问:Spring中循环依赖了解吗?
答:是一个BeanA中依赖BeanB,然后BeanB中也依赖BeanA,当然也可以是更多Bean之间的循环依赖,A依赖B,B依赖C,C依赖A这种
问:嗯,那你知道循环依赖有哪几种形式吗?
答:嗯,因为我们依赖注入的时候,可以是属性注入,也可以是构造方法注入,也可以是setter方法注入,因此对应有三种循环依赖的形式。
问:嗯,那你知道这几种循环依赖,哪些是没有问题的,哪些是程序会报错导致不能启动的吗?
答:… 好像构造依赖会报错,其他两种是不会报错的,程序是可以正常启动的。
问:嗯,那你知道为什么吗?为何构造循环依赖会报错?其他的循环依赖却可以正确的运行呢?
答:这个…好像不太清楚啊,可以要看看Bean的生命周期才知道哦…
问:嗯,那你可以简单说下Bean的生命周期吗?
答:
首先是AbstractApplicationContext的refresh方法刷新容器,然后初始化一些Bean的扩展接口,比如BeanDefinitionRegister这种,spring.factories的处理,
然后: 是InstantiationAwareBeanPostProcessor的实例化前置接口调用,他会在构造方法之前调用,构造之后还会有MergedBeanDefinitionPostProcessor处理,
然后: 就到属性填充阶段(populateBean),调用InstantiationAwareBeanPostProcessor后置接口,Autowire注入处理,
InstantiationAwareBeanPostProcessor的postProcessPropertyValues调用
然后: initializeBean进行Bean初始化,包括Aware系列接口调用,BeanPostProcessor前置方法,然后是相关的初始化方法,比如@Bean的init-method、
InitializingBean,后面BeanPostProcessor后置方法
然后:后面就是使用,销毁阶段...
问:嗯,还算完整,那你结合这个Bean的生命周期,想一下你觉得循环依赖可以正确运行Spring是如何处理的呢?
答:这个…没有研究过,我想可能会在属性注入的时候处理的吧…,还需要研究一下…
现在我们来研究一下…
@Component
public class Man {
Person person;
public Man(Person person) {
this.person = person;
}
}
@Component
public class Person {
Man man;
public Person(Man man) {
this.man = man;
}
}
报错如下:
Error creating bean with name 'man': Requested bean is currently in creation: Is there an unresolvable circular reference?
@Component
public class Man {
@Autowired
Person person;
}
@Component
public class Person {
@Autowired
Man man;
}
@Component
public class Man {
Person person;
@Autowired
public void setPerson(Person person) {
this.person = person;
}
}
@Component
public class Person {
Man man;
@Autowired
public void setMan(Man man) {
this.man = man;
}
}
ApplicationContext app = new AnnotationConfigApplicationContext(Cap12MainConfig.class);
Person person = (Person) app.getBean("person");
报错如下:
Error creating bean with name 'man': Requested bean is currently in creation: Is there an unresolvable circular reference?
//DefaultSingletonBeanRegistry
beforeSingletonCreation(beanName);
protected void beforeSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
//code2
if (earlySingletonExposure) {
if (logger.isDebugEnabled()) {
logger.debug("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
//singletonsCurrentlyInCreation是一个set集合,重复添加会返回false
!this.singletonsCurrentlyInCreation.add(beanName)
// Fail if we're already creating this bean instance:
// We're assumably within a circular reference.
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
//DefaultSingletonBeanRegistry
/** Cache of singleton objects: bean name --> bean instance */
//一级缓存,缓存的是beanName -> 单例对象,保存的是完整的Bean(已创建完成的Bean)
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** Cache of early singleton objects: bean name --> bean instance */
//二级缓存,缓存的是beanName -> 对象,保存的不完整的Bean(装配中的Bean)
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
/** Cache of singleton factories: bean name --> ObjectFactory */
//三级缓存,缓存的是beanName -> 创建Bean对象的工厂,(即将装配的Bean)
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
//DefaultSingletonBeanRegistry
//1.这个步骤是构造方法完毕之后做的,将自己添加到缓存
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
//如果一级缓存没有的话,就会把自己放到三级缓存,并清空二级缓存
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
//一级缓存没有才添加
if (!this.singletonObjects.containsKey(beanName)) {
//添加三级缓存,清空二级缓存
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
//前面的lamada表达式看起来不直观,其实是实现了一个ObjectFactory,并保存到三级缓存里面,
//后续通过ObjectFactory执行getObject的时候就会调用getEarlyBeanReference这个方法
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}
//DefaultSingletonBeanRegistry
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//1.一级缓存获取,获取不到再去二级缓存获取
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
//2.二级缓存获取,获取不到再去三级缓存
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
//3.三级缓存获取,并移动到二级缓存,并清空三级缓存
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
1.首先A初始化,然后在构造函数实例化之后,属性填充的时候,发现需要B,因此进入初始化B的流程;
2.初始化B,也和A前面一样,发现需要A就去获取A,获取A的时候,去三级缓存查询,第三级查到了,并且会调用getEarlyBeanReference,而getEarlyBeanReference会触发AOP中
核心类的父类AbstractAutoProxyCreator的getEarlyBeanReference执行,在这里会返回A的一个代理对象,由此B获得了A,B初始化成功
@Override
public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (!this.earlyProxyReferences.contains(cacheKey)) {
this.earlyProxyReferences.add(cacheKey);
}
return wrapIfNecessary(bean, beanName, cacheKey);
}
3.回到A的初始化过程,A得到一个B对象,B进行属性填充,初始化,
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) throws BeansException {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (!this.earlyProxyReferences.contains(cacheKey)) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
这里注意了,前面4.2的getEarlyBeanReference方法和这里的postProcessAfterInitialization方法都是AOP核心方法,都在AbstractAutoProxyCreator里面实现,这两个方法就是创建增强的代理对象的方法,这里是和AOP相关的知识了,并且可以保证这两个方案只会被执行一次,这里好理解,创建代理后的增强对象也只要创建一次嘛,重复创建干啥,这里是通过earlyProxyReferences这个集合来实现的,如果前面创建过集合里面就会保存,后面的方法判断集合中有就不会重复创建了。
这两个方法的调用时机,前置是循环依赖的时候,获取早起引用的时候调用,后者是正常的AOP逻辑在Bean的生命周期初始化后期BeanPostProcessor的后置方法中调用的。
下面的检查是在初始化之后
//早期引用检查
if (earlySingletonExposure) {
Object earlySingletonReference = getSingleton(beanName, false);//false表示只查二级缓存
//1.如果为null,说明二级缓存没有,说明本对象没有被循环依赖
//(被循环依赖会的话将该Bean从三级缓存移动到二级缓存)
//如果为null,没被循环依赖,就不走这个逻辑,直接走最后面返回,创建Bean完成
//不为null,说明该对象被循环依赖了,那么此时需要检查exposedObject在初始化的过程中是否有被改变
if (earlySingletonReference != null) {
//如果没改,说明初始化过程没有修改,那么就把早期引用赋值给需要返回的对象
//这里不知道为什么,难道是有可能早期引用暴露的是一个增强的代理,后面初始化就不会增强了
//因此需要将之前的增强对象返回
//另一方面,就是在前面的BeanPostProcessor里面,我们可以去在后置接口中registerSingleton往缓
//存中设置一个自定义的对象,在这里earlySingletonReference就会获取到,由此替换掉原来的对象
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 " +
"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
}
1.关闭之后,创建A不会放入缓存,那么创建B再次创建A就会提示A正在创建中,因此会报错
2.关闭该选项,对于AOP返回代理没有影响,因为在初始化完成之后发现循环依赖被关闭,则直接返回代理对象,
这一块有兴趣可以自己看看源码