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

2022-08-31 16:11:45

当你开始搞砸Spring的自动代理功能时,你经常会遇到这样的行为,如上文所述:

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

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

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

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

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

有没有人找到更好的方法来追踪这一点?


答案 1

按照这个食谱:

  1. 在 IDE 中打开(它是BeanPostProcessorCheckerAbstractApplicationContext)

  2. 在方法中设置断点if (logger.isInfoEnabled()) {postProcessAfterInitialization

  3. 运行代码

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

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

背景

想象一下这种情况:

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

当 Spring 必须创建时(因为它是 的依赖关系),它有一个问题:合同说必须全部应用于正在创建的每个 Bean。但是当春天需要时,至少有一个PP(即)还没有准备好服务!configFooPPBeanPostProcessorconfigFooPP

当您使用类来定义此 Bean 时,情况会变得更糟:@Configuration

@Configuration
public class BadSpringConfig {
     @Lazy @Bean public Config config() { return new Config(); }
     @Lazy @Bean public FooPP fooPP() { return new FooPP(); }
}

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

在此示例中,可以中断其中一个循环依赖项。你可以让实现让Spring注入后处理器。这样,您就不需要自动布线。FooPPBeanFactoryAwareBeanFactory

在代码的后面,你可以懒洋洋地要求豆子:

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

(LazyInit 的源代码)

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


答案 2

为了给这个问题带来一些结论,未初始化对象图的崩溃是由使用来获得其依赖关系引起的,而autowire机制有效地导致在我有机会对此事发表意见之前,所有其他bean定义都被初始化。解决方案是不对 BPP 使用自动布线。BeanPostProcessor@AutowiredBeanPostProcessor


推荐