Tomasz Nurkiewicz opened SPR-8609 and commented
In the following @Configuration
class assertion fails because the SpEL expression is not evaluated prior to running bar()
:
@Configuration
public class ContextConfiguration {
@Resource
private Foo foo;
@Value("#{2+3}")
private int value;
@Bean
public Bar bar() {
Assert.isTrue(value == 5, Integer.toString(value));
return new Bar();
}
}
Because cyclic dependency is introduced, bar()
is executed by the container on ContextConfiguration
that is not yet populated:
@Service
public class Foo {
@Resource
private Bar bar;
}
public class Bar {
}
I am aware that fixing this might be a bit challenging, but at least an exception should be thrown rather than silently calling @Bean
method on uninitialized @Configuration
class. Full failing test case is available here (spring-cyclic-dep-bug
branch).
Affects: 3.1 M2
Issue Links: - #13226 unresolvable circular reference when bean defined in xml config refers to bean defined in outer java config
Comment From: spring-projects-issues
Chris Beams commented
Tomasz,
This certainly makes sense, however the implementation is not terribly trivial. The only way to track this sort of thing would be via a ThreadLocal
registry of bean method invocations interacting with the enhanced CGLIB subclass @Bean
methods. It's a fair bit of overhead both at actual runtime and in general implementation complexity to achieve better error reporting for what is probably a relatively infrequent mistake.
For this reason I'm moving this to the General Backlog and marking as 'waiting-for-demand'; we'll certainly pay attention if other users weigh in here with additional comments, votes, etc.
In any case, thanks for the submission.
Comment From: spring-projects-issues
Janning Vygen commented
We had a very similar problem with circular references which was really annoying and in my opinion is a bug. I put it here as I think this relates to this issue.
@Configuration
public class ContextConfiguration {
@Resource
private Foo foo;
@Bean
public Bar bar() {
Bar bar = new Bar();
bar.setFoo(foo);
}
}
@Service
public class Foo {
@Resource
private Bar bar;
}
public class Bar {
}
this has worked fine on our development machines, but NOT on our production machines as the "bar" bean was operating with a null value of foo. So we got a NPE.
I guess that component scanning worked differently on my development machine (ubuntu/jetty) than on my production machine (debian/tomcat).
We have four operations which can run like this: - Foo.newInstance() - ContextConfiguration.inject(foo) - bar() - Foo.inject(bar)
But on our production machine spring did in a different way so we ended up with NPE because foo was null at the time bar() was called.
This should be fixed as this is not deterministic.
Comment From: bdshadow
This issue can be closed. When the example above is run, exception is thrown as expected:
2023-06-16 15:30:11.392 WARN 45054 --- [ main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'contextConfiguration': Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'foo': Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'contextConfiguration': Requested bean is currently in creation: Is there an unresolvable circular reference?
Description: The dependencies of some of the beans in the application context form a cycle: ┌─────┐ | contextConfiguration ↑ ↓ | foo └─────┘ Action: Relying upon circular references is discouraged and they are prohibited by default. Update your application to remove the dependency cycle between beans. As a last resort, it may be possible to break the cycle automatically by setting spring.main.allow-circular-references to true.
Comment From: snicoll
Yes, I agree that the failure analyzer in Spring Boot is providing a solution for this.