【问题标题】:Tracking down cause of Spring's "not eligible for auto-proxying"追查Spring“不符合自动代理条件”的原因
【发布时间】:2010-11-15 03:53:30
【问题描述】:

当您开始弄乱 Spring 的自动代理的东西时,您经常会遇到记录在案的这种行为:

实现的类 BeanPostProcessor 接口是 特殊的,所以他们被对待 因容器而异。全部 BeanPostProcessors 及其直接 引用的 bean 将被实例化 在启动时,作为特殊的一部分 的启动阶段 ApplicationContext,然后所有这些 BeanPostProcessors 将被注册 以一种有序的方式 - 并应用于 所有进一步的豆子。由于 AOP 自动代理被实现为 BeanPostProcessor 本身,没有 BeanPostProcessors 或直接 引用的 bean 有资格 自动代理(因此不会有 方面“编织”到其中。

对于任何这样的 bean,你应该看到一个 信息日志消息:“Bean 'foo' 不是 有资格被所有人处理 BeanPostProcessors(例如:不是 符合自动代理的条件)”。

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

我的问题是跟踪直接引用的位置可能非常困难,因为“直接引用”实际上可能是一个传递依赖链,最终会占用应用程序上下文中的一半 bean。 Spring 给你的只是单一的信息消息,除了告诉你一个 bean 何时被这个引用网络捕获之外,它并没有太大的帮助。

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

有没有人找到更好的方法来追踪这个问题?

【问题讨论】:

  • 您还可以获得PersistenceExceptionTranslator 类的信息消息。

标签: java spring aop


【解决方案1】:

不确定它是否有帮助,但 Eclipse Spring IDEgraph view 看起来可能有助于整理 bean 引用..

【讨论】:

    【解决方案2】:

    为了结束这个问题,未初始化对象图的崩溃是由BeanPostProcessor 使用@Autowired 获取其依赖关系引起的,并且自动装配机制有效地导致所有其他 bean 定义在我之前初始化BeanPostProcessor 有机会对此事发表意见。解决方案是不要为您的 BPP 使用自动装配。

    【讨论】:

    • 这不是解决方案;当我的后处理器需要 bean 时,我该怎么做?
    【解决方案3】:

    按照这个食谱:

    1. 在您的 IDE 中打开 BeanPostProcessorChecker(它是 AbstractApplicationContext 的内部类)

    2. 在方法postProcessAfterInitializationif (logger.isInfoEnabled()) {上设置断点

    3. 运行你的代码

    4. 当您遇到断点时,在堆栈跟踪中查找对 getBean(String,Class<T>) 的调用。

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

    背景

    想象一下这种情况:

    public class FooPP implements BeanPostProcessor {
        @Autowire
        private Config config;
    }
    

    当 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(); }
    }
    

    每个配置类都是一个 bean。这意味着从 BadSpringConfig 构建一个 bean 工厂,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(...);
    }
    

    (source for LazyInit)

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

    【讨论】:

    • 这个解决方案是否仍然适用于多线程应用程序,如 Web 服务?
    • LazyInit 是安全的,所以是的。
    • 当我将 Jersey 应用程序转换为 Spring Boot 时,我能够使用它来追踪这个问题。原来我有一个名为“conversionService”的 bean,而 ConfigurationPropertiesBindingPostProcessor 会查找具有该名称的(可选)bean。因为它是一个引用我的 bean 的 BeanPostProcessor,所以我的转换服务引用的 bean 没有正确连接(例如,它们的 @Autowired 字段没有设置)。
    • BeanPostProcessorChecker 自 Spring Framework 4 起已移至 PostProcessorRegistrationDelegate
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-20
    • 2012-08-29
    • 2012-09-05
    • 2011-12-25
    • 2019-01-09
    • 1970-01-01
    相关资源
    最近更新 更多