美文网首页
Spring循环引用实例分析

Spring循环引用实例分析

作者: 进击的蚂蚁zzzliu | 来源:发表于2021-03-17 14:54 被阅读0次

    示例代码

    @Component
    public class ClassRoom {
        private String name = "天下第一班";
        @Autowired
        private Student student;
        @Override
        public String toString() {
            return "ClassRoom{" +
                    "name='" + name + '\'' +
                    '}';
        }
    }
    
    @Component
    public class Student {
        private String name = "张三";
        @Autowired
        private ClassRoom classRoom;
        @Override
        public String toString() {
            return "Student{" +
                    "name='" + name + '\'' +
                    '}';
        }
    }
    
    @ComponentScan
    public class CircularReferencesTest {
    
        public static void main(String[] args) {
            AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
            context.register(CircularReferencesTest.class);
            context.setAllowCircularReferences(true);
            context.refresh();
    
            System.out.println(context.getBean(Student.class));
            System.out.println(context.getBean(ClassRoom.class));
    
            context.close();
        }
    }
    
    • ClassRoomStudent互相引用;
    • context.setAllowCircularReferences(true)在refresh前可以设置是否允许循环引用,默认是允许;

    源码分析

    循环引用-1.png
    • finishBeanFactoryInitialization(beanFactory)此时BeanFactory 已经创建完成,并且所有的实现了 BeanFactoryPostProcessor 接口的 Bean都已经初始化并且其中的 postProcessBeanFactory(factory) 方法已经得到回调执行了。而且 Spring 已经“手动”注册了一些特殊的 Bean,如 environment、systemProperties 等。剩下的就是初始化 singleton beans 了,如果没有设置懒加载,那么 Spring 会在这里初始化所有的 singleton beans。
    • getBean(beanName)遍历 beanDefinitionNames 通过 getBean(beanName) 去初始化 singleton beans。

    下面重点看下AbstractBeanFactory.doGetBean

    protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
            @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
        // 转换beanName,这里name有可能是别名也可能是FactoryBean
        final String beanName = transformedBeanName(name);
        Object bean;
    
        // 尝试从缓存中加载单例
        Object sharedInstance = getSingleton(beanName);
        if (sharedInstance != null && args == null) {
            ......
            // 返回对应实例,有时候存在BeanFactory的情况并不是直接返回实例本身而是返回指定方法返回的实例
            // 这里其实就是用于检测当前 bean 是否是 FactoryBean 类型,如果是则需要调用 FactoryBean 中的
            // getObject()作为返回值,例如 FactoryBean的使用
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
        }
        else {
            // 原型模式循环依赖直接抛错,只有在单例情况下才会尝试解决循环依赖,即 A 中有 B 的属性,B 中有 A的属性,
            // 依赖注入时,会产生 A 未创建完对于 B 的创建再次返回创建 A,的情况
            if (isPrototypeCurrentlyInCreation(beanName)) {
                throw new BeanCurrentlyInCreationException(beanName);
            }
            ......
            // 实例化依赖 bean 之后,开始实例化 bean 本身
            if (mbd.isSingleton()) {
                // 缓存中不存在,开始直接去加载单例 Bean
                sharedInstance = getSingleton(beanName, () -> {
                    try {
                        // getSingleton通过回调该方法,真正去创建 Bean
                        return createBean(beanName, mbd, args);
                    }
                    catch (BeansException ex) {
                        destroySingleton(beanName);
                        throw ex;
                    }
                });
                bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
            }
            ......
        }
        ......
        return (T) bean;
    }
    
    • Object sharedInstance = getSingleton(beanName)尝试从缓存中加载 bean,稍后详细分析;
    • bean = getObjectForBeanInstance(sharedInstance, name, beanName, null)返回对应实例,有时候存在 BeanFactory 的情况并不是直接返回实例本身,这里其实就是用于检测当前 bean 是否是 FactoryBean 类型,如果是则需要调用 FactoryBean 中的 getObject() 作为返回值,例如 FactoryBean的使用;
    • isPrototypeCurrentlyInCreation(beanName)当前 beanName 的原型模式的 bean 正在创建,说明原型模式存在循环依赖,直接抛出异常,只有在单例情况下才会尝试解决循环依赖;
    • sharedInstance = getSingleton缓存中不存在,开始直接去加载单例 Bean;

    小结一下就是:首先从缓存中获取 bean 实例,存在的话返回;否则通过getSingleton(String beanName, ObjectFactory<?> singletonFactory)方法并注册回调函数createBean(beanName, mbd, args)去创建 bean 实例,其中包含缓存的维护以及循环引用的处理逻辑;

    再来重点看下getSingleton(beanName)

    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        Object singletonObject = this.singletonObjects.get(beanName);
        // 缓存中不存在,并且正在创建的单例
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            synchronized (this.singletonObjects) {
                singletonObject = this.earlySingletonObjects.get(beanName);
                if (singletonObject == null && allowEarlyReference) {
                    // 当某些方法需要提前初始化的时候会调用 addSingletonFactory 将对应的
                    // ObjectFactory 初始化策略存储在 singletonFactories
                    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
                        // 调用预先设定的 getObject 方法
                        singletonObject = singletonFactory.getObject();
                        // 存储在缓存中,earlySingletonObjects 和 singletonFactories互斥
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }
        return singletonObject;
    }
    

    这里涉及3个Map1个Set

    private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));   
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
    private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
    
    1. singletonsCurrentlyInCreation存放当前正在创建的单例 bean,bean 创建完成后移除,用来判断是否正在创建;
    2. singletonObjects存放直接可用的成品 bean;
    3. earlySingletonObjects存放半成品的 bean(已经调用构造,但属性尚未赋值),提前曝光的 bean,用于解决循环依赖;
    4. singletonFactories半成品工厂,存放 ObjectFactory<?> 类型对象,用来生成半成品 bean 放入 earlySingletonObjects;

    小结一下这里的逻辑(allowEarlyReference默认为true,这里忽略):

    1. singletonObjects中获取到成品 bean 则直接返回,否则goto 2;
    2. 通过 singletonsCurrentlyInCreation判断当前 bean 是否正在创建,不是则返回 null 到 doGetBean 中进行创建,是则goto 3;
    3. earlySingletonObjects中获取提前曝光的半成品 bean,存在则直接返回,否则goto 4;
    4. singletonFactories中获取单例 bean 工厂,用来生成半成品 bean,不存在则返回null,否则goto 5;
    5. 通过预先设定的 getObject 方法生成半成品 bean,并存储放入earlySingletonObjects,由于单例bean 已经生成,则对应的 SingletonObject从singletonFactories中移除;

    再回到doGetBean看下缓存中不存在需要去创建的逻辑

    public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
        synchronized (this.singletonObjects) {
            Object singletonObject = this.singletonObjects.get(beanName);
            // 再次检查是否已经加载过
            if (singletonObject == null) {
                ......
                // 加载单例前记录加载状态,beanName 加到 singletonsCurrentlyInCreation 中
                beforeSingletonCreation(beanName);
                boolean newSingleton = false;
                ......
                try {
                    // 通过回调方法 singletonFactory 实例化 Bean
                    singletonObject = singletonFactory.getObject();
                    newSingleton = true;
                }
                ......
                finally {
                    // 加载完成 beanName 从singletonsCurrentlyInCreation 中移除
                    afterSingletonCreation(beanName);
                }
                if (newSingleton) {
                    // 添加到 singletonObjects / registeredSingletons,
                    // 从 singletonFactories / earlySingletonObjects 中删除
                    addSingleton(beanName, singletonObject);
                }
            }
            return singletonObject;
        }
    }
    
    1. beforeSingletonCreation(beanName)创建 bean 之前存入singletonsCurrentlyInCreation中,到这里如果已经存在当前 bean 则会抛出循环引用的异常,对应context.setAllowCircularReferences(false)的场景;
    Requested bean is currently in creation: Is there an unresolvable circular reference?
    
    1. singletonFactory.getObject()通过 doGetBean 中注册的回调函数创建 bean,下面详细分析;
    2. afterSingletonCreation(beanName)维护singletonsCurrentlyInCreation,即创建后从里面移除;
    3. addSingleton(beanName, singletonObject)添加到 singletonObjects,并从singletonFactories / earlySingletonObjects中删除,及bean 创建完成后加入成品Map,同时从半成品Map和半成品工厂中移除当前 bean;

    小结一下:这里就是真正去创建 bean,并且维护3个Map1个Set;

    下面再来分析回调函数中直接创建 bean的逻辑createBean(beanName, mbd, args)

    protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
            throws BeanCreationException {
        BeanWrapper instanceWrapper = null;
        ......
        if (instanceWrapper == null) {
            // 创建 Bean 实例,将 BeanDefinition 转换成 BeanWrapper
            // 1. 如果存在工厂方法则使用工厂方法进行初始化
            // 2. 一个类有多个构造方法,每个构造方法都有不同的参数,所以需要根据参数锁定构造函数并进行初始化
            // 3. 如果既不存在工厂方法也不存在带有参数的构造函数,使用默认的构造函数进行实例化
            instanceWrapper = createBeanInstance(beanName, mbd, args);
        }
        final Object bean = instanceWrapper.getWrappedInstance();
        Class<?> beanType = instanceWrapper.getWrappedClass();
        if (beanType != NullBean.class) {
            mbd.resolvedTargetType = beanType;
        }
        ......
        /**
         * 重要:::A 依赖 B,B 又依赖 A,如何解决循环依赖
         * 在 B 中创建依赖 A 时通过 ObjectFactory 提供的实例化方法来中断 A 中的属性填充,使 B 中持有的 A 仅仅是刚刚初始化并没有填充任何属性的 A,
         * 而正在初始化 A 的步骤还是在最开始创建 A 的时候进行的,但是因为A与B中的A所代表的属性地址是一样的,所以A中创建好的属性填充自然可以通过
         * B 中的A获取,这样就解决了循环依赖的问题
         */
    
        // 是否需要提早曝光:单例 & 允许循环依赖 & 当前 bean 正在创建中,检测循环依赖  allowCircularReferences默认 true
        boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
                isSingletonCurrentlyInCreation(beanName)); // DefaultSingletonBeanRegistry 类的 getSingleton 函数的 beforeSingleton 和 afterSingleton中来进行状态的记录与移除
        if (earlySingletonExposure) {
            // 为避免后期循环依赖可以在 bean 初始化完成前将创建实例的 ObjectFactory 加入 singletonFactories registeredSingletons
            addSingletonFactory(beanName, () -> {
                // 对 bean 再一次依赖引用,主要应用 SmartInstantiationAware BeanPostProcessor
                // AOP 就是在这里将 advice 动态织入 bean 中,若没有则直接返回bean,不做任何处理
                return getEarlyBeanReference(beanName, mbd, bean);
            });
        }
    
        // Initialize the bean instance.
        Object exposedObject = bean;
        try {
            // 这一步也是非常关键的,这一步负责属性装配,因为前面的实例只是实例化了,并没有设值,这里就是设值
            // 对 bean 进行填充,将各个属性值注入,其中可能存在依赖于其他bean 的属性,进行递归初始化
            populateBean(beanName, mbd, instanceWrapper);
            // 还记得 init-method 吗?还有 InitializingBean 接口?还有 BeanPostProcessor 接口?
            // 这里就是处理 bean 初始化完成后的各种回调
            exposedObject = initializeBean(beanName, exposedObject, mbd);
        }
        ......
        return exposedObject;
    }
    

    我们这里主要分析循环引用,忽略其他逻辑

    1. createBeanInstance(beanName, mbd, args)通过 BeanDefinition 获取构造函数去真正创建 bean 实例,并包装成 BeanWrapper;
    2. earlySingletonExposure是否需要提前曝光,即是否需要把这个 bean 应用半成品的逻辑;单例 & 允许循环依赖 & 当前 bean 正在创建中;
    3. addSingletonFactory维护半成品工厂和半成品Map,半成品工厂获取 bean 通过回调函数getEarlyBeanReference生成,这里默认返回上面创建的 bean,也可以通过实现SmartInstantiationAwareBeanPostProcessor接口进行维护;
    this.singletonFactories.put(beanName, singletonFactory);
    this.earlySingletonObjects.remove(beanName);
    
    1. populateBean对创建的 bean 进行初始化,对 bean 进行填充,将各个属性值注入,其中可能存在依赖于其他bean 的属性(例如@Autowired),最终通过调用getBean方法进行递归创建;

    小结一下:这里首先去创建 bean,然后判断是否需要经过半成品 -> 成品的流程,需要的话构造一个ObjectFactory放入singletonFactories,供后面通过缓存获取 bean 时使用(doGetBean中getSingleton(beanName))。

    实例分析

    循环引用-2.png

    可以运行上面示例代码,在下面几个地方加上条件断点(beanName.equals("classRoom") || beanName.equals("student"))进行debug

    AbstractBeanFactory#doGetBean - Object sharedInstance = getSingleton(beanName);
    AbstractBeanFactory#doGetBean - sharedInstance = getSingleton(beanName, () -> {...});
    AbstractAutowireCapableBeanFactory#doCreateBean - if (earlySingletonExposure)
    DefaultSingletonBeanRegistry#getSingleton - afterSingletonCreation(beanName);
    DefaultSingletonBeanRegistry#getSingleton - addSingleton(beanName, singletonObject);
    

    总结

    • Spring 默认支持单例 bean 的循环引用,也可以通过设置context.setAllowCircularReferences(true)关闭循环引用;
    • Spring 通过3个Map1个Set来解决循环引用的问题,大致思路就如下:

    1.在加载 bean 之前先放到singletonsCurrentlyInCreation中表示该bean已经正在创建;
    2.如果允许循环引用则先构造单例泛型bean工厂 ObjectFactory<?>放入singletonFactories
    3.然后在具体碰到循环引用时取出刚才的ObjectFactory<?>创建循环引用的实例,然后暂存在earlySingletonObjcets表示半成品bean,因为此时尚未进行初始化其依赖的属性尚未赋值;
    4.解决了这个循环引用的问题,这时内部被依赖的 bean就可以顺利初始化完成并加入到singletonObjcets这个成品bean集合中(后续getBean都是直接从这里面获取);
    5.内部依赖bean构建完成继续回去完成外层bean的初始化(这时才对依赖bean的属性进行赋值),然后也放到singletonObjcets中;

    相关文章

      网友评论

          本文标题:Spring循环引用实例分析

          本文链接:https://www.haomeiwen.com/subject/gkidcltx.html