追踪Spring的"不符合自动代理条件"的原因

ska*_*man 48 java aop spring

当你开始搞乱Spring的自动代理东西时,你经常遇到记录的这种行为:

实现BeanPostProcessor接口的类是特殊的,因此容器对它们的处理方式不同.所有BeanPostProcessors及其直接引用的bean将在启动时实例化,作为ApplicationContext的特殊启动阶段的一部分,然后所有这些BeanPostProcessors将以排序方式注册 - 并应用于所有其他bean.由于AOP自动代理是作为BeanPostProcessor本身实现的,因此没有BeanPostProcessors或直接引用的bean可以进行自动代理(因此不会将方面'编织'到它们中.

对于任何此类bean,您应该看到一条信息日志消息:"Bean'foo'不适合所有BeanPostProcessors处理(例如:不符合自动代理条件)".

换句话说,如果我编写自己的BeanPostProcessor,并且该类直接引用上下文中的其他bean,那么这些引用的bean将不符合自动代理的条件,并且会记录一条消息.

我的问题是跟踪直接引用的位置可能非常困难,因为"直接引用"实际上可以是一系列传递依赖,最终占用应用程序上下文中的一半bean.Spring提供的只是单个信息消息,并且除了告诉您何时在这个引用网中捕获了bean之外,它并没有太多帮助.

我正在开发的BeanPostProcessor确实有对其他bean的直接引用,但它是一组非常有限的引用.尽管如此,根据日志消息,我的上下文中的几乎每个bean都被排除在自动代理之外,但我无法看到依赖性发生在哪里.

有没有人找到更好的方法来跟踪这个?

Aar*_*lla 36

请遵循以下食谱:

  1. BeanPostProcessorChecker在IDE中打开(它是内部类AbstractApplicationContext)
  2. if (logger.isInfoEnabled()) {在方法中设置断点postProcessAfterInitialization
  3. 运行你的代码
  4. 当您点击断点时,查找getBean(String,Class<T>)堆栈跟踪中的调用.

    其中一个调用将尝试创建一个BeanPostProcessor.那豆应该是罪魁祸首.

背景

想象一下这种情况:

public class FooPP implements BeanPostProcessor {
    @Autowire
    private Config config;
}
Run Code Online (Sandbox Code Playgroud)

当Spring必须创建config(因为它是它的依赖FooPP)时,它有一个问题:契约表明所有BeanPostProcessor必须应用于正在创建的每个bean.但是当Spring需要时config,至少有一个PP(即FooPP)尚未准备好服务!

当您使用@Configuration类来定义此bean 时,这会变得更糟:

@Configuration
public class BadSpringConfig {
     @Lazy @Bean public Config config() { return new Config(); }
     @Lazy @Bean public FooPP fooPP() { return new FooPP(); }
}
Run Code Online (Sandbox Code Playgroud)

每个配置类都是一个bean.这意味着要构建一个bean工厂BadSpringConfig,Spring需要应用后处理器,fooPP但为了做到这一点,它首先需要bean工厂......

在此示例中,可以破坏其中一个循环依赖项.你可以制作FooPP工具BeanFactoryAware让Spring注入BeanFactory后处理器.这样,您就不需要自动装配了.

稍后在代码中,你可以懒得问一下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(...);
}
Run Code Online (Sandbox Code Playgroud)

(LazyInit的来源)

要打破bean工厂和后处理器之间的循环,您需要在XML配置文件中配置后处理器.Spring可以读取并构建所有结构而不会混淆.


ska*_*man 24

只是为了给这个问题带来一些结束,未初始化的对象图的崩溃是由于BeanPostProcessor使用它@Autowired来获取它的依赖性而引起的,并且autowire机制有效地导致所有其他bean定义在我BeanPostProcessor有机会发表声明之前被初始化.物.解决方案不是为您的BPP使用自动装配.

  • 这不是解决方案; 当我在后处理器中需要bean时,我该怎么办? (5认同)

Tim*_*Tim 5

不确定是否有帮助,但是Eclipse Spring IDE图形视图看起来可能有助于整理bean引用。