Spring JavaConfig
  1. Spring JavaConfig
  2. SJC-94

Circular dependencies in Javaconfig raise StackOverflow Error

    Details

    • Type: Bug Bug
    • Status: Closed
    • Priority: Major Major
    • Resolution: Won't Fix
    • Affects Version/s: 1.0.0.M3
    • Fix Version/s: None
    • Component/s: Internals
    • Labels:
      None
    • Environment:
      Windows Vista Pro, JDK6

      Description

      I a new to Javaconfig (but not to Spring), and I encounter issues with circular dependencies.

      Here is my problem:

      I have two classes A and B with mutual dependency :

      public class A {

      private B b;

      public B getB()

      { return b; }

      public void setB(B b)

      { this.b = b; }

      }

      public class B {

      private A a;

      public A getA()

      { return a; }

      public void setA(A a)

      { this.a = a; }

      }

      I have built a Configuration class as follows:

      @Configuration
      public class TestLoad {

      @Bean
      public A a() {
      A a = new A();
      a.setB(b());
      return a;
      }

      @Bean
      public B b()

      { B b = new B(); b.setA(a()); return b; } }

      And I instantiate the application context with the following code:

      JavaConfigApplicationContext context = new JavaConfigApplicationContext(TestLoad.class);

      When executing this last piece of code, I get the following error (which actually never stops occurring):

      Caused by: java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException:
      ... (100+ times)
      java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: java.lang.StackOverflowError
      at org.springframework.config.java.core.StandardBeanM ethodProcessor.createNewOrGetCachedSingletonBean(S tandardBeanMethodProcessor.java:85)
      at org.springframework.config.java.enhancement.cglib. BeanMethodMethodInterceptor.returnWrappedResultMay BeCached(BeanMethodMethodInterceptor.java:59)
      at org.springframework.config.java.enhancement.cglib. BeanMethodMethodInterceptor.intercept(BeanMethodMe thodInterceptor.java:52)
      at test.TestLoad$$EnhancerByCGLIB$$9f00849b.a(<genera ted>)
      at test.TestLoad.b(TestLoad.java:22)
      at test.TestLoad$$EnhancerByCGLIB$$9f00849b.CGLIB$b$1 (<generated>)
      at test.TestLoad$$EnhancerByCGLIB$$9f00849b$$FastClas sByCGLIB$$546c3d84.invoke(<generated>)
      at net.sf.cglib.proxy.MethodProxy.invokeSuper(MethodP roxy.java:167)
      at org.springframework.config.java.enhancement.cglib. BeanMethodMethodInterceptor$1.invokeOriginalClass( BeanMethodMethodInterceptor.java:65)
      at org.springframework.config.java.core.MethodBeanWra pper.wrapResult(MethodBeanWrapper.java:118)
      at org.springframework.config.java.core.StandardBeanM ethodProcessor.createNewOrGetCachedSingletonBean(S tandardBeanMethodProcessor.java:82)
      ... 958 more

      Also, when I change TestLoad to use ConfigurationSupport and getBean (code below):

      @Configuration
      public class TestLoad extends ConfigurationSupport {

      @Bean
      public A a() {
      A a = new A();
      a.setB(getBean(B.class));
      return a;
      }

      @Bean
      public B b()

      { B b = new B(); b.setA(getBean(A.class)); return b; } }

      I get the following exception:

      Exception in thread "main" org.springframework.beans.factory.BeanCreationExce ption: Error creating bean with name 'a' defined in Bean creation method a in class test.TestLoad: Instantiation of bean failed; nested exception is org.springframework.beans.factory.BeanDefinitionSt oreException: Factory method [public final test.A test.TestLoad$$EnhancerByCGLIB$$9cfb512d.a()] threw exception; nested exception is java.lang.RuntimeException: org.springframework.beans.factory.BeanCreationExce ption: Error creating bean with name 'b' defined in Bean creation method b in class test.TestLoad: Instantiation of bean failed; nested exception is org.springframework.beans.factory.BeanDefinitionSt oreException: Factory method [public final test.B test.TestLoad$$EnhancerByCGLIB$$9cfb512d.b()] threw exception; nested exception is java.lang.RuntimeException: org.springframework.beans.factory.NoSuchBeanDefini tionException: No unique bean of type [test.A] is defined:

      The question is: does Javaconfig support circular references at least in the same way the XML config does (for setters but not constructors)?

      Thaks for any help on this,

      Kind regards,

      Michel.

        Issue Links

          Activity

          Hide
          Michel Jeanson added a comment -

          I attach a very succinct Eclipse project highlighting the issue.
          Depndencies may have to be updated to suit your Eclipse environment.
          /M

          Show
          Michel Jeanson added a comment - I attach a very succinct Eclipse project highlighting the issue. Depndencies may have to be updated to suit your Eclipse environment. /M
          Hide
          Chris Beams added a comment -

          Hi Michel,

          "The question is: does Javaconfig support circular references at least in the same way the XML config does (for setters but not constructors)?"

          The simplest answer to this question is 'no'.

          Because you are creating the object manually, and calling the setters programmatically, JavaConfig cannot handle circular references quite as elegantly as XML-driven config can.

          There are, however, some reasonable workarounds for the problem:

          1) Consider using @Bean(autowired=Autowired.BY_TYPE)

          If you configure your @Bean definitions to be autowired, then JavaConfig can handle circular refrences a bit better:

          @Bean(autowired=Autowired.BY_TYPE)
          public A a()

          { return new A(); }

          @Bean(autowired=Autowired.BY_TYPE)
          public B b()

          { return new B(); }

          In the above, both instances will be created and then set on each other by the container. This avoids the circular reference problem entirely.

          As a second option, you could use @AutoBean, which takes instantiation out of your hands entirely:

          @AutoBean
          public abstract A a();

          @AutoBean
          public abstract B b();

          I'm going to leave this bug open for the moment, as I consider other alternatives, and how robust the cyclic reference detection / error handling should be. I'd be very interested to hear if either of the above approaches satisfy your needs.

          Thanks!

          Show
          Chris Beams added a comment - Hi Michel, "The question is: does Javaconfig support circular references at least in the same way the XML config does (for setters but not constructors)?" The simplest answer to this question is 'no'. Because you are creating the object manually, and calling the setters programmatically, JavaConfig cannot handle circular references quite as elegantly as XML-driven config can. There are, however, some reasonable workarounds for the problem: 1) Consider using @Bean(autowired=Autowired.BY_TYPE) If you configure your @Bean definitions to be autowired, then JavaConfig can handle circular refrences a bit better: @Bean(autowired=Autowired.BY_TYPE) public A a() { return new A(); } @Bean(autowired=Autowired.BY_TYPE) public B b() { return new B(); } In the above, both instances will be created and then set on each other by the container. This avoids the circular reference problem entirely. As a second option, you could use @AutoBean, which takes instantiation out of your hands entirely: @AutoBean public abstract A a(); @AutoBean public abstract B b(); I'm going to leave this bug open for the moment, as I consider other alternatives, and how robust the cyclic reference detection / error handling should be. I'd be very interested to hear if either of the above approaches satisfy your needs. Thanks!
          Hide
          Chris Beams added a comment -

          Michel,

          After further thought, I'm resolving this as Won't Fix. Based on comments above and further consideration, direct programmatic circular dependencies are a kind of 'natural limitation' for JavaConfig. There are however, a number of ways to work around that limitation. I've added a test case that demonstrates these various approaches, see org.springframework.config.java.CircularDependenciesTests for details.

          Regards,

          • Chris
          Show
          Chris Beams added a comment - Michel, After further thought, I'm resolving this as Won't Fix. Based on comments above and further consideration, direct programmatic circular dependencies are a kind of 'natural limitation' for JavaConfig. There are however, a number of ways to work around that limitation. I've added a test case that demonstrates these various approaches, see org.springframework.config.java.CircularDependenciesTests for details. Regards, Chris
          Hide
          Michel Jeanson added a comment -

          Thank you Chris for your feedback and your work.
          I shall work around the issue.
          Rgds, Michel.

          Show
          Michel Jeanson added a comment - Thank you Chris for your feedback and your work. I shall work around the issue. Rgds, Michel.

            People

            • Assignee:
              Chris Beams
              Reporter:
              Michel Jeanson
            • Votes:
              0 Vote for this issue
              Watchers:
              0 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved:

                Time Tracking

                Estimated:
                Original Estimate - Not Specified
                Not Specified
                Remaining:
                Remaining Estimate - 0d
                0d
                Logged:
                Time Spent - 2.5h
                2.5h

                  Development