追查Spring“不符合自动代理条件”的原因

Posted

技术标签:

【中文标题】追查Spring“不符合自动代理条件”的原因【英文标题】:Tracking down cause of Spring's "not eligible for auto-proxying" 【发布时间】: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 类的信息消息。 【参考方案1】:

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

【讨论】:

【参考方案2】:

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

【讨论】:

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

按照这个食谱:

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

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

    运行你的代码

    当您遇到断点时,在堆栈跟踪中查找对 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

以上是关于追查Spring“不符合自动代理条件”的原因的主要内容,如果未能解决你的问题,请参考以下文章

Linux下利用coredump技术追查进程崩溃原因

linux C使用strerror来追查错误信息

MySQL主从同步机制与同步延时问题追查过程

Spring-batch - 交易失败 - 我怎么弄清楚什么是错的?

小记一次Kafka集群响应慢问题追查

Spring aop