3

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?

3
  • how do you initialize service1 ? Commented Aug 16, 2011 at 14:45
  • via property i edited description Commented Aug 16, 2011 at 14:50
  • if you don't set the bean2 dependency on the bean1 and invoke your method does it work ? Commented Aug 16, 2011 at 14:57

2 Answers 2

5

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.

Sign up to request clarification or add additional context in comments.

2 Comments

Yes, so i made initialization of needed service via constructor and bean2 via property. And it works. I understand that this is bad design, but i have no time currently for refactoring :)
@user253202: it doesn't look like the refactoring should be terrible, just combine bean1 and bean2.
0

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>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.