zoukankan      html  css  js  c++  java
  • Spring是如何解决循环依赖的?

    Get Started

    首先我们新建了 Maven 项目,并且在 pom.xml 文件中新增了依赖

    <!-- https://mvnrepository.com/artifact/org.springframework/spring-beans -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>${spring.version}</version>
    </dependency>
    

    这个依赖是 2020年7月的最新引用,你可以从 中央仓库 获取最新的依赖。

    由于我们选择引用了 spring-beans 没有引用 spring-context 依赖,自然也就没有 ApplicationContext 接口,没有 @Serivce@Component 注解,因此我们更加专注地分析 Spring 究竟是怎样解决循环依赖的。

    循坏依赖

    我们在用户服务中引用商品服务:

    public class UserService {
        private GoodsService goodsService;
    }
    

    我们在商品服务中引用用户服务:

    public class GoodsService {
        private UserService userService;
    }
    

    两者相互依赖,就像下图所示:

    探究如何 getBean

    Object ApplicationContext.getBean(Class cls) 是我们在项目中常用的获取 Bean 对象的方法,其本质是调用 beanFactory.getBean(),而 beanFactory 的常用对象就是 DefaultListableBeanFactory

    下面我们就来研究一下怎么用 DefaultListableBeanFactory 来获取 Bean 对象。

    出于对 getBean() 方法的熟悉,我们“熟练”地写出了下面的代码:

    // 注意:这是一个会报错的常见错误示例
    @Test
    public void test1() {
          DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
          UserService bean = factory.getBean(UserService.class);
          System.out.println(bean);
    }
    

    遗憾的是,代码运行时抛出了 NoSuchBeanDefinitionException,这个时候我注意到 BeanDefinitionRegistry 接口,这是 DefaultListableBeanFactory 实现的一个接口。
    这个接口提供了注册 BeanDefinition 的方法-void registerBeanDefinition(String name, BeanDefinition beanDefinition), 于是我们改写代码:

    // 这个例子中 userService 对象属性 goodsService 为 null,不算正确
    @Test
    public void createBeanTest() {
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        RootBeanDefinition def = new RootBeanDefinition(UserService.class);// 添加 BeanDefinition
        factory.registerBeanDefinition("userService", def); // 为工厂类注册 BeanDefinition
        UserService bean = factory.getBean(UserService.class);
        System.out.println(bean);
    }
    

    我这次用 debug 模式,断点断在最后一行的输出语句上,然后 Evaluate 得到如下图的结果:

    很不幸,这次没能正常填充属性 goodsService,于是我们再次调用 RootBeanDefinition.setPropertyValues(MutablePropertyValues propValues) 改写代码:

    // 还是一段错误的代码示例
    @Test
    public void createBeanTest() {
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        RootBeanDefinition def = new RootBeanDefinition(UserService.class);
        // 新增代码:填充属性 goodsService
        def.setPropertyValues(new MutablePropertyValues().add("goodsService", new RuntimeBeanReference(GoodsService.class)));
        factory.registerBeanDefinition("userService", def);
        UserService bean = factory.getBean(UserService.class);
        System.out.println(bean);
    }
    

    控制台情况下:

    如图所示,这次抛出了 BeanCreationException,该异常发生在 UserService 填充属性 goodsService 的时候。嵌套的异常还是 NoSuchBeanDefinitionException,主要原因是没有 GoodsService 对应的 BeanDefinition,我们又又又改写一次:

    // 这是一段正确的代码
    @Test
    public void test() {
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        {
            RootBeanDefinition userServiceDef = new RootBeanDefinition(UserService.class);
            userServiceDef.setPropertyValues(new MutablePropertyValues()
                    .add("goodsService", new RuntimeBeanReference("goodsService")));
            factory.registerBeanDefinition("userService", userServiceDef);
        }
        {
            RootBeanDefinition goodsServiceDef = new RootBeanDefinition(); // 为商品类GoodsService补充定义
            goodsServiceDef.setBeanClass(GoodsService.class);
            goodsServiceDef.setPropertyValues(new MutablePropertyValues()
                    .add("userService", new RuntimeBeanReference("userService")));
            factory.registerBeanDefinition("goodsService", goodsServiceDef);
        }
        factory.getBean(UserService.class);
    }
    

    如图所示,再抛异常:

    错误原因:缺少 setter 方法,我们需要修正一下 UserService 和 GoodsService 类,给他们增加 setter 方法,修正如下:

    public class UserService {
        private GoodsService goodsService;
        // 修正:增加 setter 方法
        public void setGoodsService(GoodsService goodsService) {
            this.goodsService = goodsService;
        }
    }
    public class GoodsService {
        private UserService userService;
        // 修正:增加 setter 方法
        public void setUserService(UserService userService) {
            this.userService = userService;
        }
    }
    

    我们再次debug,把断点打在最后一行,然后 Evaluate。我们最终得到了期望结果——“无限套娃”(即循环依赖):

    Bean 的创建过程

    经过上面一系列的操作,想必大家已经看出了一些端倪,创建 Bean 好像是下面这个过程:

    上面这张图结合了我们先前探究的过程,我们有了一个初步的印象,Bean 的创建过程似乎可以简化为如下过程:

    • 实例化 Bean,简单理解就是 UserService bean = new UserService()
    • 填充属性,即 bean.setGoodsService(new GoodsService())
    • 后置代理,就是在 Bean 外面再包装一层 BeanProxy proxy = new BeanProxy(bean); proxy.doSomething();
    • 添加到实例池: Map<String, Object> instances = new HashMap<>(); instances.put("userService", bean);

    Spring 创建 Bean 的简化流程

    首先我们来看一下 DefaultListableBeanFactory 类继承结构图:

    Spring 创建Bean的简化过程如下图所示:

    • 首先调用公开方法 DefaultListableBeanFactory.getBean(Class cls) 来获取 Bean 对象,通过一系列操作转化 class 对象为 beanName。
    • 得到 beanName 之后,调用 AbstractBeanFactory.getBean(String name, Class cls) , 在这个方法中又会调用受保护的 AbstractBeanFactory.doGetBean 方法
    • doGetBean 方法中,又会调用 DefaultSingletonBeanFactory.getSingleton(String beanName, ObjectFactory factory)
    • getSingleton 方法中,首先在单例池 DefaultSingletonBeanFactory.singletonObjects 查找是否已经存在 beanName 对应的单例对象。如果找不到,那会调用函数式接口 ObjectFactory 来创建一个对象,而这个对象工厂调用的函数是一个受保护的抽象方法 AbstractBeanFactory.crateBean(String beanName, RootBeanDefinition def, Object[] args)
    • AbstractAutowireCapableBeanFactory 实现了超类的 createBean 方法, 并在 createBean 方法中调用了受保护的方法 AbstractAutowireCapableBeanFactory.doCreateBean
    • doCreateBean方法中,还会调用 AbstractAutowireCapableBeanFactory.populateBean 来填充属性。
    • populateBean 填充属性时,还可能遇上没有创建过的对象,因此还可能递归调用 AbstractBeanFactory.getBean(String beanName)

    小结一下:
    AbstractAutowireCapableBeanFactory 负责了实例化 Bean,填充属性,后置处理的任务,DefaultSingletonBeanRegistry 负责了单例池的维护工作。

    Spring 解决循环依赖


    解决循环依赖,关键还得看注册中心中三个方法:添加单例工厂、获取未完成的单例以及添加到单例池。首先我们需要了解一下注册中心三个重要的成员变量:

    我们把断点打在 DefaultSingletonBeanRegistry.getSingleton 方法内:

    protected Object getSingleton(String beanName, boolean allowEarlyReferenc
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null && this.isSingletonCurrentlyInCreation(be
            synchronized(this.singletonObjects) {
                singletonObject = this.earlySingletonObjects.get(beanName);
                if (singletonObject == null && allowEarlyReference) {
                    ObjectFactory<?> singletonFactory = (ObjectFactory)this.s
                    if (singletonFactory != null) {
                        singletonObject = singletonFactory.getObject();
                        this.earlySingletonObjects.put(beanName, singletonObj
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }
        return singletonObject;
    }
    
    • 第一次调用发生 doGetBean 中,查询单例池中是否已经存在 UserService bean 对象,即调用 getSingleton("userService" /*beanName*/, true /*allowEarlyReference*/),方法返回 null。
    • 没有查询到单例,因此先调用 DefaultSingletonBeanRegistry.beforeSingletonCreation("userService" /*beanName*/),调用后正在创建的单例对象新增了 "userService"
    • 然后调用 getSingleton("userService" /*beanName*/, ObjectFactory<?> singletonFactory) 获取并创建 UserService bean 对象。
    • 紧接着调用 doCreateBean 方法,并在该方法中调用 addSingletonFactory("userService" /*beanName*/, ObjectFactory<?> singletonFactory) 需要警惕的是,这里的 ObjectFactory 匿名类调用的方法是 AbstractAutowireBeanFactory.getEarlyBeanReference,通过该工厂创建的是未完成的 bean 对象。
    • 再接着就该调用 populateBean 方法,并且在该方法中调用 applyPropertyValues ,欲为 UserService bean 对象设置 goodsService 属性
    • 再次调用 doGetBean,查询单例池中是否已经存在 GoodsService bean 对象,即调用 getSingleton("goodsService" /*beanName*/, true /*allowEarlyReference*/),方法返回 null。
    • 执行 DefaultSingletonBeanRegistry.beforeSingletonCreation("goodsService" /*beanName*/) 后,正在创建的单例对象新增了 "goodsService"
    • 接着就该获取和创建 GoodsService bean 对象了,和创建 UserService bean 对象类似,在调用 addSingletonFactory 方法后,单例工厂集发生了变化,注册单例集也发生了变化:
    • 在为 GoodsService bean 对象设置 userService 属性时,再一次进入了 getSingleton("userService" /*beanName*/, true /*allowEarlyReference*/),但是这次,从单例工厂集合中取出了 "userService" 对应的 ObjectFactory 对象,创建了一个 未完成的 UserService Bean对象,放到了未完成单例集中,同时从单例工厂中移除了"userService"对应的 ObjectFactory 工厂。
    • 紧接着 GoodsService Bean 对象先调用了 afterSingletonCreation("goodsService" /*beanName*/),移出了正在创建单例集
    • 然后就移除工厂集中的 ObjectFactory 对象,并将 GoodsService Bean 放入单例池:
    • 然后 UserService Bean 对象也成功设置了 goodsService 属性,完成创建,紧接着调用 afterSingletonCreationaddSingleton 方法

    Spring 如何解决循环依赖的?


    总结,Spring 就是靠获取未完成的Bean对象来填充属性,解决循环依赖的。

    为什么不直接把对象放入 earlySingletonObjects,而是先放入 singletonFactories 中?
    创建对象是需要消耗性能的,假如创建没有循环依赖的对象,是不需要创建未完成的Bean对象的,所以不是直接创建未完成对象放入未完成单例集 earlySingletonObjects,而是把未完成单例工厂放入 singletonFactories
    比如以下这段代码,在运行时就不会调用 getSingleton 中的 ObjectFactory.getObject() 方法来创建 TestService 未完成 Bean 对象。

    // Bean 对象类
    public class TestService {
        String name;
    
        public void setName(String name) {
            this.name = name;
        }
        public String getName() {
            return name;
        }
    }
    
    public class CreateBeanTest {
        @Test
        public void testCreateTestService() {
            DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
            RootBeanDefinition definition = new RootBeanDefinition(TestService.class);
            definition.setPropertyValues(new MutablePropertyValues().add("name", "admin"));
            factory.registerBeanDefinition("testService", definition);
            TestService service = factory.getBean(TestService.class);
            System.out.println(service.getName());
        }
    }
    

    * 超类是如何调用子类的方法的?

    补充一个问题:超类 DefaultSingletonBeanRegistry 是如何调用子类 AbstractAutowireCapableBeanFactory 中的 doGetBean 方法的?

    // AbstractBeanFactroy.doGetBean 方法中的一段代码
    sharedInstance = this.getSingleton(beanName, () -> {
        try {
            return this.createBean(beanName, mbd, args);
        } catch (BeansException exp) {
            this.destroySingleton(beanName);
            throw exp;
        }
    });
    

    对于 AbstractBeanFactroy 而言, createBean 是一个需要子类实现的抽象方法

    // AbstractBeanFactory 类中的抽象方法 createBean
    protected abstract Object createBean(String var1, RootBeanDefinition var2, @Nullable Object[] var3) throws BeanCreationException;
    

    然后我们来看 DefaultSingletonBeanRegistry 获取并创建单例的方法

    // 节选 DefaultSingletonBeanRegistry.getSingleton(String beanName, ObjectFactory<?> singletonFactory) 方法中一段代码
    try {
        singletonObject = singletonFactory.getObject();
        newSingleton = true;
    } catch (IllegalStateException exp1) {
        // ... (省略)
    } catch (BeanCreationException exp2) {
        // ... (省略)
    }
    

    因此 AbstractBeanFactroy 通过 ObjectFactory.getObject() 间接调用了 createBean 方法,而子类 AbstractAutowireCapableBeanFactory实现了 createBean 方法

    // AbstractAutowireCapableBeanFactory.createBean 方法中的一段代码
    beanInstance = this.doCreateBean(beanName, mbdToUse, args);
    

    小结一下:
    超类 DefaultSingletonBeanRegistry 是在函数式接口 ObjectFactory 的匿名实现类中,调用抽象方法 createBean
    再由子类 AbstractAutowireCapableBeanFactory 实现抽象方法,来达到超类调用子类方法的目的。

  • 相关阅读:
    改变UIAlertController的标题、内容的字体和颜色
    mac 常用软件
    office web apps server 问题和解决办法
    如何在Excel中启用宏?
    System.Drawing.Image.Save(Savepath),保存为jpg格式,参数错误,文件0kb解决办法
    asp.net 1.1网站开发配置出现”Visual Studio .NET 无法创建或打开应用程序”解决方法
    map 遍历
    Java统计List集合中每个元素出现的次数
    sql 片段写法
    循环依赖
  • 原文地址:https://www.cnblogs.com/kendoziyu/p/how-does-spring-fix-circular-reference.html
Copyright © 2011-2022 走看看