追查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
的内部类)
在方法postProcessAfterInitialization
if (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“不符合自动代理条件”的原因的主要内容,如果未能解决你的问题,请参考以下文章