spring中如何处理循环依赖

发布于 2024-11-30 03:21:11 字数 1081 浏览 0 评论 0原文

例如,我有两个 bean:

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;
   }

}

现在,如果我尝试按以下方式在 spring 中配置它们:

<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没有完全初始化?

For example i have two beans:

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;
   }

}

Now if i try to configure them in spring in the folllowing way:

<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>

Init method of bean2 is executed. Bean2 has injected bean1, but bean1 itself is not fully initialized so calling bean1.doStuff() that will call service1.doActualStuff() will return into NPE. Why bean1 is not fully initialized?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

琉璃繁缕 2024-12-07 03:21:11

Spring 以未完全初始化的状态缓存单例 bean,以便注入到否则会成为无法解析的循环引用的位置。在您的情况下,初始化顺序将是这样的:

  1. 实例化 bean1 (意味着仅调用构造函数,而不是 init 方法)
  2. 将 bean1 添加到单例缓存以处理循环依赖关系
  3. 开始注入 bean1 的依赖关系
  4. 实例化 bean2 以满足 bean1 的依赖关系
  5. 将 bean2 添加到singleton 缓存处理循环依赖关系
  6. 开始注入 bean2 的依赖关系——其中之一是缓存的 bean1 实例,该实例尚未完全初始化
  7. 完成注入bean2 的依赖
  8. 调用 bean2 的 init 方法——呃! bean1 还没有初始化!
  9. 完成创建 bean2
  10. (如果您确实做到了这一点...) 完成注入 bean1 的依赖项
  11. bean1 上没有 init 方法,但这就是调用它的地方
  12. 完成创建 bean1

考虑重新考虑您的设计以理清 bean1 和 bean2 之间的依赖关系。

Spring caches singleton beans in a not-fully-initialized state for injection into places that would otherwise be an unresolvable circular reference. In your case, the initialization order would be something like this:

  1. Instantiate bean1 (meaning call the constructor only, not init methods)
  2. Add bean1 to the singleton cache to handle circular dependencies
  3. Start injecting bean1's dependencies
  4. Instantiate bean2 to satisfy bean1's dependency
  5. Add bean2 to the singelton cache to handle circular dependencies
  6. Start injecting bean2's dependencies--one of these is the cached bean1 instance, which is still not fully initialized
  7. Finish injecting bean2's dependencies
  8. Call bean2's init method--uhoh! bean1 still isn't initted yet!
  9. Done creating bean2
  10. (If you actually made it this far...) Finish injecting bean1's dependencies
  11. No init method on bean1, but this is where it would be called
  12. Done creating bean1

Consider rethinking your design to untangle the dependencies between bean1 and bean2.

栖竹 2024-12-07 03:21:11

如果以编程方式注入第一个 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>

How about if you inject the first bean programatically:

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);
   }
}

Remove the first injection from your 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>
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文