美文网首页
百行代码理解Spring循环依赖解决方案

百行代码理解Spring循环依赖解决方案

作者: 万物始 | 来源:发表于2020-01-19 23:44 被阅读0次

    1. 什么是循环依赖

    Spring核心是由容器完成两件事:1.创建对象,2.装配对象的依赖。在创建对象的过程中,会为其属性赋值,例如有两个bean,X和Y,X中有属性y,Y中有属性x。x的创建过程中,设置属性y,而此时y不存在,进而去创建y,创建y的过程中,设置属性x,发现x不存在(x正在创建中)…… 如果这样一直下去,互相依赖的对象都不能被创建出来。这就是所谓的循环依赖。

    2. 代码演示

    下面是一个手写的demo,演示对容器启动过程:涵盖包扫描、bena创建、bean的循环依赖的处理。Spring的整个过程比较复杂,比如:bean是在原生对象的基础做了包裹,创建bean的过程有许许多多其他处理。demo对这些都做了简化。
    前往github查看源码 https://github.com/nothingax/spring-circular-references-demo

    2.1 容器启动

    模拟容器的启动过程,主要两步操作,包扫描、创建bean实例,并存入单例池中

    /** 单例池,即容器,维护最终生成的单例对象,{bean的类名:bean实例} */
    private Map<String, Object> singletonObjects = new HashMap<>();
    
    /** 存储【bean获取函数】的map:{bean名称:获取bean实例的函数} */
    private Map<String, ObjectFactory> singletonFactories = new HashMap<>();
    
    @Test
        public void circularReferencesTest() throws Exception {
            // 1、包扫描获取包下的类
            List<String> classNames = this.componentScan("com.demo.circularreferences.object");
            // 2、bean创建
            for (String className : classNames) {
                this.createBean(className);
            }
    
            X x = (X) singletonObjects.get(X.class.getName());
            Y y = (Y) singletonObjects.get(Y.class.getName());
            assert x != null && y != null && x.getY() != null && y.getX() != null;
        }
    

    2.2 bean的创建

    创建过程中设置bean的属性,在设置属性之前会将bean的引用(这里是一个获取gean的函数)放入到singletonFactories中,这一步操作是解决循环依赖的关键。

    
    private Object createBean(String className) throws Exception {
      // 从单例池中获取bean对象,如果存在直接返回
      Object singleton = singletonObjects.get(className);
      if (singleton != null) {
        return singleton;
      }
    
      // 创建原生对象
      Object instance = Class.forName(className).newInstance();
    
      // 暴露方式为存储一个获取bean的函数到map中,
      // 将【获取bean的函数】放入singletonFactories map中,这是解决循环依赖的关键。bean是提前暴露,此时bean仍没有完成创建,但拿到它的引用就足够了
      // 用函数而不是简单的bean对象,因为函数更灵活,可以插入其他的操作,比如aop
      singletonFactories.put(className, () -> {
        // TODO 对 instance 的其他操作
        return instance;
      });
    
      // 依赖装配:设置instance的属性
      this.handleFieldInject(instance);
      // bean创建完成,存入单例池中
      singletonObjects.put(className, instance);
      return instance;
    }
    

    2.3 属性注入

    先从单例池singletonObjects取,如果取不到则从singletonFactories中取,如果存在会取出获取bean的函数,对该函数求值,就拿到了先前的bean。仍然拿不到的话则调用createBean创建。

    private void handleFieldInject(Object instance) throws Exception {
            Field[] declaredFields = instance.getClass().getDeclaredFields();
            for (Field field : declaredFields) {
                // 为标注@CAutowired注解的属性赋值,CAutowired是自定义注解,模拟Spring中@Autowired
                if (field.isAnnotationPresent(CAutowired.class)) {
                    // 获取属性类名
                    String fieldClassName = field.getType().getName();
                    field.setAccessible(true);
    
                    // 从单例池中获取字段的实例
                    Object singleton = singletonObjects.get(fieldClassName);
                    if (singleton != null) {
                        field.set(instance, singleton);
                    } else if (singletonFactories.containsKey(fieldClassName)) {
                        // 单例池中不存在,则从singletonFactories中获取
                        ObjectFactory objectFactory = singletonFactories.get(fieldClassName);
                        // 对函数求值,执行前面代码传入的lambda表达式
                        Object object = objectFactory.getObject();
                        field.set(instance, object);
                        singletonFactories.remove(fieldClassName);
                    } else {
                        // singletonFactories也没有则创建
                        Object injectObj = this.createBean(fieldClassName);
                        field.set(instance, injectObj);
                    }
                }
            }
        }
    

    3. Spring循环依赖的解决方案

    Spring是支持循环依赖的,是如何做到的呢?设计主要如下,三个map,文档称为缓存,其中singletonObjects和earlySingletonObjects与上面demo中的含义相同,earlySingletonObjects的作用是为了在增加一层缓存,当lambda表达式执行过一次后就会将结果放进去,避免了为多次函数求值,提升了性能。

    /** Cache of singleton objects: bean name to bean instance. */
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
    
    /** Cache of singleton factories: bean name to ObjectFactory. */
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
    
    /** Cache of early singleton objects: bean name to bean instance. */
    private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
    

    【获取bean的函数】存入singletonFactoriesmap中,该函数在java中的表现是ObjectFactory,一个函数式接口

    
    // org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
    // 创建bean的过程中,添加【获取bean的函数】到 singletonFactories中
    addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    

    4. 就是这样,欢迎讨论

    相关文章

      网友评论

          本文标题:百行代码理解Spring循环依赖解决方案

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