如何处理spring中的循环依赖

时间:2011-08-16 14:43:13

标签: java spring dependency-injection

例如我有两个豆子:

class Bean1 {
  private SomeService service1;
  private SomeService servive2;
  private Bean2 bean2;

  public void doStuff() {
     service1.doActualStuff();
  }

  public void setBean2(Bean2 bean2) {
     this.bean2 = bean2;
  }

  public Bean2 getBean2() { return this.bean2 }
}

class Bean2 {
   private Bean1 bean1;
   private SomeService3 service3;

   public void init() {
       bean1.doStuff();
   }

   public void setBean1(Bean1 bean1) {
      this.bean1 = bean1;
   }

}

现在,如果我尝试按照以下方式在春季配置它们:

<bean id="service1" class="SomeService">
    ...
</bean>
<bean id="bean1" class="Bean1">
   <property name="bean2" ref="bean2"/>
   <property name="service1" ref="service1"/>
   ...
</bean>
<bean id="bean2" class="Bean2" init-method="init">
   <property name="bean1" ref="bean1"/>
   ...
</bean>

执行bean2的init方法。 Bean2已注入bean1,但bean1本身未完全初始化,因此调用service1.doActualStuff()的bean1.doStuff()将返回NPE。为什么bean1没有完全初始化?

2 个答案:

答案 0 :(得分:5)

Spring将未完全初始化状态的单例bean缓存到可能无法解析的循环引用的位置。在您的情况下,初始化顺序将是这样的:

  1. 实例化bean1(意味着只调用构造函数,而不是init方法)
  2. 将bean1添加到单例缓存中以处理循环依赖
  3. 开始注入bean1的依赖项
  4. 实例化bean2以满足bean1的依赖性
  5. 将bean2添加到singelton缓存以处理循环依赖
  6. 开始注入bean2的依赖项 - 其中一个是缓存的bean1实例,但仍未完全初始化
  7. 完成注入bean2的依赖项
  8. 调用bean2的init方法 - 喔! bean1还没有初始化!
  9. 完成创建bean2
  10. (如果你真的这么做了......)完成注入bean1的依赖
  11. bean1上没有init方法,但这就是所谓的
  12. 完成创建bean1
  13. 考虑重新思考你的设计,以解开bean1和bean2之间的依赖关系。

答案 1 :(得分:0)

如果以编程方式注入第一个bean,那该怎么样:

class Bean2 {
   private Bean1 bean1;
   private SomeService3 service3;

   public void init() {
       bean1.doStuff();
   }

   public void setBean1(Bean1 bean1) {
      this.bean1 = bean1;

      //HERE
      this.bean1.setBean2(this);
   }
}

从spring xml中删除第一次注射:

<bean id="service1" class="SomeService">
    ...
</bean>
<bean id="bean1" class="Bean1">
   <!-- NOT NEEDED ANYMORE <property name="bean2" ref="bean2"/> -->
   <property name="service1" ref="service1"/>
   ...
</bean>
<bean id="bean2" class="Bean2" init-method="init">
   <property name="bean1" ref="bean1"/>
   ...
</bean>