• 3.3 Spring5源码---循环依赖过程中spring读取不完整bean的最终解决方案


    根据之前解析的循环依赖的源码, 分析了一级缓存,二级缓存,三级缓存的作用以及如何解决循环依赖的. 然而在多线程的情况下, Spring在创建bean的过程中, 可能会读取到不完整的bean. 下面, 我们就来研究两点:

    1. 为什么会读取到不完整的bean.

    2. 如何解决读取到不完整bean的问题.

    和本文相关的spring循环依赖的前两篇博文如下: 

    3.1 spring5源码系列--循环依赖 之 手写代码模拟spring循环依赖

    3.2spring源码系列----循环依赖源码分析


    一. 为什么会读取到不完整的bean.

    我们知道, 如果spring容器已经加载完了, 那么肯定所有bean都是完整的了, 但如果, spring没有加载完, 在加载的过程中, 构建bean就有可能出现不完整bean的情况

    如下所示: 

    首先, 有一个线程要去创建A类, 调用getBean(A),他会怎么做呢?

    第一步: 调用getSingleton()方法, 去缓存中取数据, 我们发现缓存中啥都没有, 肯定返回null. 

    第二步: 将其放入到正在创建集合中,标记当前bean A正在创建

    第三步: 实例化bean

    第四步: 将bean放到三级缓存中. 定义一个函数接口, 方便后面调用

    addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

    第四步: 属性赋值. 在属性赋值的时候, 返现要加载类B,就在这个时候, 另一个线程也进来了, 要创建Bean A.

    第五步: 线程2 创建bean ,也是先去调用getSinglton()从缓存中取, 一二级换粗中都没有,但是三级缓存中却是有的. 于是就调用动态代理, 去创建bean, 很显然这时候创建的bean是不完整的. 然后将其放入到二级缓存中, 二级缓存里的bean也是不完整的. 这就导致了后面是用的bean可能都是不完整的. 详细的分析上图

    二. 如何解决读取到不完整bean的问题.

    其实, 之所以出现这样的问题, 原因就在于, 第一个bean还没有被创建完, 第二个bean就开始了. 这是典型的并发问题. 

    针对这个问题, 其实,我们加锁就可以了.  

     用自己手写的代码为例

    第一: 将整个创建过程加一把锁

    /**
         * 获取bean, 根据beanName获取
         */
        public static Object getBean(String beanName) throws Exception {
    
            // 增加一个出口. 判断实体类是否已经被加载过了
            Object singleton = getSingleton(beanName);
            if (singleton != null) {
                return singleton;
            }
            Object instanceBean;
            synchronized (singletonObjects) {
    
                // 标记bean正在创建
                if (!singletonsCurrectlyInCreation.contains(beanName)) {
                    singletonsCurrectlyInCreation.add(beanName);
                }
    
                /**
                 * 第一步: 实例化
                 * 我们这里是模拟, 采用反射的方式进行实例化. 调用的也是最简单的无参构造函数
                 */
                RootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionMap.get(beanName);
                Class<?> beanClass = beanDefinition.getBeanClass();
                // 调用无参的构造函数进行实例化
                instanceBean = beanClass.newInstance();
    
    
                /**
                 * 第二步: 放入到三级缓存
                 * 每一次createBean都会将其放入到三级缓存中. getObject是一个钩子方法. 在这里不会被调用.
                 * 什么时候被调用呢?
                 * 在getSingleton()从三级缓存中取数据, 调用创建动态代理的时候
                 */
                singletonFactories.put(beanName, new ObjectFactory() {
                    @Override
                    public Object getObject() throws BeansException {
                        return new JdkProxyBeanPostProcessor().getEarlyBeanReference(earlySingletonObjects.get(beanName), beanName);
                    }
                });
                //earlySingletonObjects.put(beanName, instanceBean);
    
                /**
                 *  第三步: 属性赋值
                 *  instanceA这类类里面有一个属性, InstanceB. 所以, 先拿到 instanceB, 然后在判断属性头上有没有Autowired注解.
                 *  注意: 这里我们只是判断有没有Autowired注解. spring中还会判断有没有@Resource注解. @Resource注解还有两种方式, 一种是name, 一种是type
                 */
                Field[] declaredFields = beanClass.getDeclaredFields();
                for (Field declaredField : declaredFields) {
                    // 判断每一个属性是否有@Autowired注解
                    Autowired annotation = declaredField.getAnnotation(Autowired.class);
                    if (annotation != null) {
                        // 设置这个属性是可访问的
                        declaredField.setAccessible(true);
                        // 那么这个时候还要构建这个属性的bean.
                        /*
                         * 获取属性的名字
                         * 真实情况, spring这里会判断, 是根据名字, 还是类型, 还是构造函数来获取类.
                         * 我们这里模拟, 所以简单一些, 直接根据名字获取.
                         */
                        String name = declaredField.getName();
    
                        /**
                         * 这样, 在这里我们就拿到了 instanceB 的 bean
                         */
                        Object fileObject = getBean(name);
    
                        // 为属性设置类型
                        declaredField.set(instanceBean, fileObject);
                    }
                }
    
    
                /**
                 * 第四步: 初始化
                 * 初始化就是设置类的init-method.这个可以设置也可以不设置. 我们这里就不设置了
                 */
    
    
                /**
                 * 第五步: 放入到一级缓存
                 *
                 * 在这里二级缓存存的是动态代理, 那么一级缓存肯定也要存动态代理的实例.
                 * 从二级缓存中取出实例, 放入到一级缓存中
                 */
                if (earlySingletonObjects.containsKey(beanName)) {
                    instanceBean = earlySingletonObjects.get(beanName);
                }
                singletonObjects.put(beanName, instanceBean);
    
                // 删除二级缓存
    
                // 删除三级缓存
            }
            return instanceBean;
        }

    然后在从缓存取数据的getSingleton()上也加一把锁

    private static Object getSingleton(String beanName) {
            //先去一级缓存里拿,
            Object bean = singletonObjects.get(beanName);
            // 一级缓存中没有, 但是正在创建的bean标识中有, 说明是循环依赖
            if (bean == null && singletonsCurrectlyInCreation.contains(beanName)) {
                synchronized (singletonObjects) {
                    bean = earlySingletonObjects.get(beanName);
                    // 如果二级缓存中没有, 就从三级缓存中拿
                    if (bean == null) {
                        // 从三级缓存中取
                        ObjectFactory objectFactory = singletonFactories.get(beanName);
                        if (objectFactory != null) {
                            // 这里是真正创建动态代理的地方.
                            bean = objectFactory.getObject();
                            // 然后将其放入到二级缓存中. 因为如果有多次依赖, 就去二级缓存中判断. 已经有了就不在再次创建了
                            earlySingletonObjects.put(beanName, bean);
                        }
                    }
                }
            }
            return bean;
        }

    加了两把锁.

    这样, 在分析一下

    如上图,线程B执行到getSingleton()的时候, 从一级缓存中取没有, 到二级缓存的时候就加锁了,他要等待直到线程A完成执行完才能进入. 这样就避免出现不完整bean的情况. 

    三. 源码解决

    在创建实例bean的时候, 加了一把锁, 锁是一级缓存.

     1 public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
     2         Assert.notNull(beanName, "Bean name must not be null");
     3         synchronized (this.singletonObjects) {
     4             // 第一步: 从一级缓存中获取单例对象
     5             Object singletonObject = this.singletonObjects.get(beanName);
     6             if (singletonObject == null) {
     7                 if (this.singletonsCurrentlyInDestruction) {
     8                     throw new BeanCreationNotAllowedException(beanName,
     9                             "Singleton bean creation not allowed while singletons of this factory are in destruction " +
    10                             "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
    11                 }
    12                 if (logger.isDebugEnabled()) {
    13                     logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
    14                 }
    15                 // 第二步: 将bean添加到singletonsCurrentlyInCreation中, 表示bean正在创建
    16                 beforeSingletonCreation(beanName);
    17                 boolean newSingleton = false;
    18                 boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
    19                 if (recordSuppressedExceptions) {
    20                     this.suppressedExceptions = new LinkedHashSet<>();
    21                 }
    22                 try {
    23                     // 第三步: 这里调用getObject()钩子方法, 就会回调匿名函数, 调用singletonFactory的createBean()
    24                     singletonObject = singletonFactory.getObject();
    25                     newSingleton = true;
    26                 }
    27                 catch (IllegalStateException ex) {
    28                     // Has the singleton object implicitly appeared in the meantime ->
    29                     // if yes, proceed with it since the exception indicates that state.
    30                     singletonObject = this.singletonObjects.get(beanName);
    31                     if (singletonObject == null) {
    32                         throw ex;
    33                     }
    34                 }
    35                 catch (BeanCreationException ex) {
    36                     if (recordSuppressedExceptions) {
    37                         for (Exception suppressedException : this.suppressedExceptions) {
    38                             ex.addRelatedCause(suppressedException);
    39                         }
    40                     }
    41                     throw ex;
    42                 }
    43                 finally {
    44                     if (recordSuppressedExceptions) {
    45                         this.suppressedExceptions = null;
    46                     }
    47                     afterSingletonCreation(beanName);
    48                 }
    49                 if (newSingleton) {
    50                     addSingleton(beanName, singletonObject);
    51                 }
    52             }
    53             return singletonObject;
    54         }
    55     }

    再从缓存中取数据的时候, 也加了一把锁, 和我们的demo逻辑是一样的. 锁也是一级缓存.

    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
            // 从一级缓存中获取bean实例对象
            Object singletonObject = this.singletonObjects.get(beanName);
            /**
             * 如果在第一级的缓存中没有获取到对象, 并且singletonsCurrentlyIncreation为true,也就是这个类正在创建.
             * 标明当前是一个循环依赖.
             *
             * 这里有处理循环依赖的问题.-- 我们使用三级缓存解决循环依赖
             */
            if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
                synchronized (this.singletonObjects) {
                    /**
                     * 从二级缓存中拿bean, 二级缓存中的对象是一个早期对象
                     * 什么是早期对象?就是bean刚刚调用了构造方法, 还没有给bean的属性进行赋值, 和初始化, 这就是早期对象
                      */
    
                    singletonObject = this.earlySingletonObjects.get(beanName);
                    if (singletonObject == null && allowEarlyReference) {
                        /**
                         * 从三级缓存拿bean, singletonFactories是用来解决循环依赖的关键所在.
                         * 在ios后期的过程中, 当bean调用了构造方法的时候, 把早期对象包装成一个ObjectFactory对象,暴露在三级缓存中
                          */
                        ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                        if (singletonFactory != null) {
                            /**
                             * 在这里通过暴露的ObjectFactory包装对象. 通过调用他的getObject()方法来获取对象
                             * 在这个环节中会调用getEarlyBeanReference()来进行后置处理
                             */
                            singletonObject = singletonFactory.getObject();
                            // 把早期对象放置在二级缓存中
                            this.earlySingletonObjects.put(beanName, singletonObject);
                            // 删除三级缓存
                            this.singletonFactories.remove(beanName);
                        }
                    }
                }
            }
            return singletonObject;
        }
  • 相关阅读:
    bzoj 1031: [JSOI2007]字符加密Cipher
    python re模块实现计算器
    python sys模块和序列化模块
    python time时间模块
    python 发红包的小程序
    python ranndom模块及生成验证码
    python os模块练习题
    python os模块常用命令
    快速排序,归并排序
    选择排序和插入排序
  • 原文地址:https://www.cnblogs.com/ITPower/p/13972220.html
Copyright © 2020-2023  润新知