详解春天中如何控制2个bean中的初始化顺序

  

开发过程中有这样一个场景,2个bean初始化逻辑中有依赖关系,需要控制二者的初始化顺序。实现方式可以有多种,本文结合目前对春天的理解,尝试列出几种思路。

  

<>强场景

  

假设A、B两个bean都需要在初始化的时候从本地磁盘读取文件,其中B加载的文件,依赖一个中加载的全局配置文件中配置的路径,所以需要一个先于B初始化,此外一个中的配置改变后也需要触发B的重新加载逻辑,所以A、B需要注入彼此。

  

对于下面的模型,问题简化为:我们需要initA()先于initB()得到执行。

        @ service   公共类{   @ autowired   私人B B;      公众(){   system . out。println(“构造”);   }      @PostConstruct   公共空间init () {   initA ();   }      私人空间initA () {   system . out。println (“init”);   }   }      @ service   公共类B {   @ autowired   私人的;      公共B () {   system . out。println (" B构造”);   }      @PostConstruct   公共空间init () {   initB ();   }      私人空间initB () {   system . out。println (“B init”);   }   }      之前      

<强>方案一:立旗

  

我们可以在业务层自己控制A, B的初始化顺序,在一个中设置一个”是否初始化的“标记,B初始化前检测一个是否得以初始化,如果没有则调用一个的初始化方法,所谓的check-and-act。对于上述模型,实现如下:

        @ service   公共类{      私人静态不稳定的布尔值初始化;      @ autowired   私人B B;      公众(){   system . out。println(“构造”);   }      @PostConstruct   公共空间init () {   initA ();   }      公共布尔isInitialized () {   返回初始化;   }      公共空间initA () {   如果(! isInitialized ()) {   system . out。println (“init”);   }   初始化=true;   }   }      @ service   公共类B {      @ autowired   私人的;         公共B () {   system . out。println (" B构造”);   }      @PostConstruct   公共空间init () {   initB ();   }         私人空间initB () {   如果(! a.isInitialized ()) {   a.initA ();   }   system . out。println (“B init”);   }      之前      

执行效果:

  

构建
  B构建
  一个init
  B init
  

  

这种立旗的方法好处是可以做到延迟初始化,但是如果类似逻辑很多的话代码中到处充斥着类似代码,不优雅,所以考虑是否框架本身就可以满足我们的需要。

  

<强>方案二:使用取决于

  

中春天的取决于注解可以保证被依赖的豆先于当前bean被容器创建,但是如果不理解春天中豆加载过程会对取决于有误解,自己也确实踩过坑。对于上述模型,如果在B上加上注解@DependsOn ({a}),得到的执行结果是:

  

构建
  B构建
  B init
  一个init
  

  

在这里问题的关键是:豆属性的注入是在初始化方法调用之前。

     //代码位置:AbstractAutowireCapableBeanFactory.doCreateBean//填充bean的各个属性,包括依赖注入   populateBean (beanName mbd instanceWrapper);   如果(exposedObject !=null) {//调用初始化方法,如果是InitializingBean则先调用afterPropertiesSet然后调用自定义的init方法方法   exposedObject=initializeBean (beanName exposedObject mbd);   }      

结合本例,发生的实际情况是,因为出现了循环依赖,一个依赖B,加载B, B依赖,所以得到了一个提前暴露的,然后调用B的初始化方法,接着回到一个的初始化方法。具体源码分析过程如下:

  

ApplicationContext在刷新过程中的最后会加载所有的没有懒惰的单例。

  

详解春天中如何控制2个bean中的初始化顺序

  

本例中,先加载的bean,最终通过无参构造器构造,然后,继续属性填充(populateBean),发现需要注入bean B所以转而加载bean B(递归调用getBean())。此时发现bean B需要取决于(“A”),在保存依赖关系(为了防止循环取决于)后,调用getBean (“A”),此时会得到提前暴露的bean,所以继续B的加载,流程为:初始化策略构造实例→属性填充(同样会注入提前暴露的bean)→调用初始化方法。

详解春天中如何控制2个bean中的初始化顺序