Follow this recipe:
Open BeanPostProcessorChecker
in your IDE (it's an inner class of AbstractApplicationContext
)
Set a breakpoint on if (logger.isInfoEnabled()) {
in the method postProcessAfterInitialization
Run your code
When you hit the breakpoint, look for calls to getBean(String,Class<T>)
in your stack trace.
One of these calls will try to create a BeanPostProcessor
. That bean should be the culprit.
Background
Imagine this situation:
public class FooPP implements BeanPostProcessor {
@Autowire
private Config config;
}
When Spring has to create config
(since it's a dependency of FooPP
), it has a problem: The contract says that all BeanPostProcessor
must be applied to every bean that is being created. But when Spring needs config
, there is at least one PP (namely FooPP
) which isn't ready for service!
This gets worse when you use an @Configuration
class to define this bean:
@Configuration
public class BadSpringConfig {
@Lazy @Bean public Config config() { return new Config(); }
@Lazy @Bean public FooPP fooPP() { return new FooPP(); }
}
Every configuration class is a bean. That means to build a bean factory from BadSpringConfig
, Spring needs to apply the post-processor fooPP
but in order to do that, it first needs the bean factory ...
In this example, it's possible to break one of the cyclic dependencies. You can make FooPP
implement BeanFactoryAware
to get Spring inject the BeanFactory
into the post processor. That way, you don't need autowiring.
Later in the code, you can lazily ask for the bean:
private LazyInit<Config> helper = new LazyInit<Config>() {
@Override
protected InjectionHelper computeValue() {
return beanFactory.getBean( Config.class );
}
};
@Override
public Object postProcessBeforeInitialization( Object bean, String beanName ) throws BeansException {
String value = helper.get().getConfig(...);
}
(source for LazyInit)
To break the cycle between the bean factory and the post processor, you need to configure the post processor in an XML config file. Spring can read that and build all the structures without getting confused.
Just to bring some closure to this question, the collapse of the uninitialized object graph was caused by the BeanPostProcessor
using @Autowired
to get its dependencies, and the autowire mechanism effectively caused every other bean definition to be initialized before my BeanPostProcessor
got a chance to have a say in the matter. The solution is not to use autowiring for your BPPs.
Not sure if it's of any help, but the Eclipse Spring IDE's graph view looks like it could be helpful in sorting out bean references..
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With