Spring Boot 源码分析(五):循环依赖

259 阅读5分钟

循环依赖就是bean之间相互引用,如用户bean与角色bean相互持有对方的引用,IOC容器创建好用户bean后要为角色属性赋值,然后就要新建一个角色bean然后为其属性赋值后再提供给用户bean使用,但这时角色bean的属性包括用户。这就是循环依赖的问题,IOC容器已经做好了设计解决这个问题。解决方法主要是加了3级缓存。

  • singletonObjects:一级缓存,保存完全初始化好的bean。
  • earlySingletonObjects:二级缓存,保存创建好但没完成属性赋值的bean。
  • singletonFactories,三级循环,存放用来获取单例bean的工厂。
  • singletonsCurrentlyInCreation,存放正在创建的bean的名称。

这几个是DefaultSingletonBeanRegistry的成员属性,DefaultListableBeanFactory继承后,容器就有了管理循环依赖的能力。

考察下基于@Autowired的循环依赖

通过一个案例来说明这种情况。创建用户类User和角色类Role,User中用@Autowired注入Role类型属性,Role中用@Autowired注入User类型属性。然后添加测试类,创建ApplicationConxt时会刷新容器,触发bean创建。

@Component
public class User {

    @Autowired
    Role role;
}

@Component
public class Role {

    @Autowired
    User user;
}

测试类:

package org.cosmos.spring.circular;

public class App {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext("org.cosmos.spring.circular");
    }
}

1.1 初始化Role

IOC容器先初始化字母表排序靠前的Role

1.1.1 getSingleton

执行流程:preInstantiateSingletons->getBean->doGetBean,doGetBean执行到getSingleton时有控制循环依赖的地方。

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
        Assert.notNull(beanName, "Bean name must not be null");
        synchronized(this.singletonObjects) {
            Object singletonObject = this.singletonObjects.get(beanName);
            if (singletonObject == null) {
                //...
                
                //控制循环依赖
                this.beforeSingletonCreation(beanName);
                //...
                
                 try {
                    singletonObject = singletonFactory.getObject();
                    newSingleton = true;
                } //catch...finally...

beforeSingletonCreation会检查当前创建的bean是否在singletonsCurrentlyInCreation中,如果存在则说明当前对象在一个创建流程中出现2次,这时抛出异常。

    protected void beforeSingletonCreation(String beanName) {
        if (!this.inCreationCheckExclusions.contains(beanName) &&   	                    !this.singletonsCurrentlyInCreation.add(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }
    }

第一次把role的bean名称添加进singletonsCurrentlyInCreation。

1.1.2 早期引用的处理

getSingleton会执行ObjectFactory参数的getObject方法,也就是执行createBean真正创建role。createBean->doCreateBean,doCreateBean中用createBeanInstance创建出刚实例化但尚未进行属性赋值的role,然后有对早期引用的处理。这里的早期,指的是bean刚创建出来属性没赋值。

//...
        boolean earlySingletonExposure = mbd.isSingleton() && this.allowCircularReferences && this.isSingletonCurrentlyInCreation(beanName);
        if (earlySingletonExposure) {
			//...
            this.addSingletonFactory(beanName, () -> {
                return this.getEarlyBeanReference(beanName, mbd, bean);
            });
        }

//...

判断条件:单例、允许出现循环依赖(当前版本默认允许)、在singletonsCurrentlyInCreation集合中。满足条件进入if分支。

    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);
            }

        }
    }

方法内部把创建role对象的工厂保存到三级缓存singletonFactories中,并从二级缓存中移除。

1.1.3 属性赋值

populateBean为role对象的属性赋值,用所有后置处理器的postProcessProperties时,很明显CommonAnnotationBeanPostProcessor不会收集到@Resource等标记的属性,AutowiredAnnotationBeanPostProcessor会收集到@Autoweired标注的属性。

1-9.jpg

CommonAnnotationBeanPostProcessor继续调inject方法,执行流程:inject->resolveFieldValue->beanFactory的resolveDependency方法。

 protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
            Field field = (Field)this.member;
            Object value;
     		try{
                //...
           		 value = this.resolveFieldValue(field, bean, beanName);
            }//catch...
 }


 private Object resolveFieldValue(Field field, Object bean, @Nullable String beanName) {
            DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
            desc.setContainingClass(bean.getClass());
            //...
            
            Object value;
            try {
                value = AutowiredAnnotationBeanPostProcessor.this.beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
            } // catch...

这里是DefaultListableBeanFactory,继续执行resolveDependency->doResolveDependency。

public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName, @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
        //...
                if (result == null) {
                    result = this.doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
                }
               return result;
            }

public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName, @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
    //...
    if (instanceCandidate instanceof Class) {
         instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
    }
    //...
}              

doResolveDependency中调DependencyDescriptor的resolveCandidate方法,resolveCandidate的第一个参数是doResolveDependency解析出来的role对象的属性user。

1-10.jpg

这个方法果然是去容器中去取user对象了。

1.2 初始化Uer

创建user对象和创建role一样,都是执行getBean->doGetBean,也会把user放入singletonsCurrentlyInCreation集合中。继续createBean->doCreateBean,同样有针对uer的早期引用处理,把role对象的工厂保存到三级缓存中并从二级缓存中移除。

最后到了属性赋值环节,因为User类用了@Autowired注入了role,所以CommonAnnotationBeanPostProcessor处理方法和上面一样,最终getBean(role)。

1.2.1 再次调用getBean获取role

执行getBean(role)到doGetBean,会调getSingleton(role)。这个重载方法是循环。

    public Object getSingleton(String beanName) {
        return this.getSingleton(beanName, true);
    }

    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
            //从二级缓存找,双重检查锁保证线程安全
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
                synchronized(this.singletonObjects) {
                    singletonObject = this.singletonObjects.get(beanName);
                    if (singletonObject == null) {
                        singletonObject = this.earlySingletonObjects.get(beanName);
                        if (singletonObject == null) {
                            //从三级缓存找
                            ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
                            if (singletonFactory != null) {
                                //刚开时创建role时,早期引用处理时把role放到了三级缓存
                                singletonObject = singletonFactory.getObject();
                                //role的三级缓存删了,并放入二级缓存
                                this.earlySingletonObjects.put(beanName, singletonObject);
                                this.singletonFactories.remove(beanName);
                            }
                        }
                    }
                }
            }
        }

        return singletonObject;
    }
1.2.2 Role初始化之后

通过getSingleton获取到role对象后,getBean(role)执行完毕,返回获得到role对象。这时user还处于属性注入阶段。回到CommonAnnotationBeanPostProcessor处理@Autoweired标注字段的处理逻辑中。

 protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
            Field field = (Field)this.member;
            Object value;
            if (this.cached) {
                try {
                    value = AutowiredAnnotationBeanPostProcessor.this.resolvedCachedArgument(beanName, this.cachedFieldValue);
                } // catch ...
            } else {
                value = this.resolveFieldValue(field, bean, beanName);
            }

            if (value != null) {
                //获取到role后,用反射将role设置到User的属性中
                ReflectionUtils.makeAccessible(field);
                field.set(bean, value);
            }

        }

接着user的populateBean和initializeBean方法继续执行,这里和循环依赖无关。doCreateBean执行完返回createBean,然后返回getSingleton:

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
        Assert.notNull(beanName, "Bean name must not be null");
        synchronized(this.singletonObjects) {
            Object singletonObject = this.singletonObjects.get(beanName);
            if (singletonObject == null) {
				//...
                this.beforeSingletonCreation(beanName);
                boolean newSingleton = false;

                try {
                    //user对象创建完成后返回这里
                    singletonObject = singletonFactory.getObject();
                    newSingleton = true;
                } //catch ...
                 finally {
                    //...
                    this.afterSingletonCreation(beanName);
                }

                if (newSingleton) {
                    this.addSingleton(beanName, singletonObject);
                }
            }

            return singletonObject;
        }
    }

通过singletonFactory的getObject返回user对象后,执行afterSingletonCreation,从singletonsCurrentlyInCreation集合中删除user。

    protected void afterSingletonCreation(String beanName) {
       if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)){
            //throw ...
        }
    }

然后执行addSingleton,user对象放入一级缓存,并从二三级缓存中移除。最终一个user对象完全创建出来了。

    protected void addSingleton(String beanName, Object singletonObject) {
        synchronized(this.singletonObjects) {
            //放入一级缓存
            this.singletonObjects.put(beanName, singletonObject);
            //从二级和三级缓存中移除
            this.singletonFactories.remove(beanName);
            this.earlySingletonObjects.remove(beanName);
            this.registeredSingletons.add(beanName);
        }
    }

1.3 回到Role的创建

user对象创建出来并完成属性赋值和全部初始化了,返回到role对象的属性赋值,此时会将完全创建好的user对象注入,然后完成后面的populateBean和initializeBean。后面和user对象一样,返回singletonFactory方法执行afterSingletonCreation和addSingleton,把role对象从singletonsCurrentlyInCreation集合中删除、放入一级缓存并从二三级缓存中移除。至此,循环依赖的问题解决了,两个对象都完全创建好了。

通过一副图,快速抓住循环依赖的精髓:

循环依赖.jpg