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?
Best Answer
Follow this recipe:
Open
BeanPostProcessorChecker
in your IDE (it's an inner class ofAbstractApplicationContext
)Set a breakpoint on
if (logger.isInfoEnabled()) {
in the methodpostProcessAfterInitialization
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:
When Spring has to create
config
(since it's a dependency ofFooPP
), it has a problem: The contract says that allBeanPostProcessor
must be applied to every bean that is being created. But when Spring needsconfig
, there is at least one PP (namelyFooPP
) which isn't ready for service!This gets worse when you use an
@Configuration
class to define this bean:Every configuration class is a bean. That means to build a bean factory from
BadSpringConfig
, Spring needs to apply the post-processorfooPP
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
implementBeanFactoryAware
to get Spring inject theBeanFactory
into the post processor. That way, you don't need autowiring.Later in the code, you can lazily ask for the bean:
(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.