Question

When you start messing around with Spring's auto-proxy stuff, you often run into this behaviour as documented:

Classes that implement the BeanPostProcessor interface are special, and so they are treated differently by the container. All BeanPostProcessors and their directly referenced beans will be instantiated on startup, as part of the special startup phase of the ApplicationContext, then all those BeanPostProcessors will be registered in a sorted fashion - and applied to all further beans. Since AOP auto-proxying is implemented as a BeanPostProcessor itself, no BeanPostProcessors or directly referenced beans are eligible for auto-proxying (and thus will not have aspects 'woven' into them.

For any such bean, you should see an info log message: “Bean 'foo' is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)”.

In other words, if I write my own BeanPostProcessor, and that class directly references other beans in the context, then those referenced beans will not be eligible for auto-proxying, and a message is logged to that effect.

My problem is that tracking down where that direct reference is can be very difficult, since the "direct reference" can in fact be a chain of transitive dependencies that ends up taking in half the beans in the application context. All Spring gives you is that single info message, and it's not really much help, beyond telling you when a bean has been caught in this web of references.

The BeanPostProcessor I'm developing does have direct references to other beans, but it's a very limited set of references. Despite this, pretty much every bean in my context is then being excluded from being auto-proxied, according to the log messages, but I can't see where that dependency is happening.

Has anyone found a better way of tracking this down?

Was it helpful?

Solution 2

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.

OTHER TIPS

Follow this recipe:

  1. Open BeanPostProcessorChecker in your IDE (it's an inner class of AbstractApplicationContext)
  2. Set a breakpoint on if (logger.isInfoEnabled()) { in the method postProcessAfterInitialization
  3. Run your code
  4. 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.

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

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top