1. 概述

在本篇文章中,我们将一起探讨 Spring 中常见的 “not eligible for auto-proxying” 警告的成因,并给出解决方案。

首先,我们通过一个实际的例子来复现这个警告信息。接着,分析其背后的原因,最后给出修复方案并验证效果。

2. “not eligible for auto-proxying” 警告产生的原因

2.1 示例配置

我们先从一个简单的示例入手,模拟出这个警告是如何在 Spring 应用启动过程中出现的。

首先,我们定义一个自定义注解 @RandomInt,用于标记需要注入随机整数的字段:

@Retention(RetentionPolicy.RUNTIME)
public @interface RandomInt {
    int min();
    int max();
}

然后创建一个 DataCache 类,它是一个 Spring 组件。我们希望为这个缓存分配一个随机的分组(例如用于分片):

@Component
public class DataCache {
    @RandomInt(min = 2, max = 10)
    private int group;
    private String name;
}

接着我们创建 RandomIntGenerator 类,用于生成指定范围内的随机整数:

@Component
public class RandomIntGenerator {
    private Random random = new Random();
    private DataCache dataCache;

    public RandomIntGenerator(DataCache dataCache) {
        this.dataCache = dataCache;
    }

    public int generate(int min, int max) {
        return random.nextInt(max - min) + min;
    }
}

⚠️ 注意:这里我们通过构造器注入的方式将 DataCache 注入到了 RandomIntGenerator 中。

最后,我们创建 RandomIntProcessor 类,它实现了 BeanPostProcessor 接口,用于在 Bean 初始化前处理带有 @RandomInt 注解的字段:

public class RandomIntProcessor implements BeanPostProcessor {
    private final RandomIntGenerator randomIntGenerator;

    public RandomIntProcessor(RandomIntGenerator randomIntGenerator) {
        this.randomIntGenerator = randomIntGenerator;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        Field[] fields = bean.getClass().getDeclaredFields();
        for (Field field : fields) {
            RandomInt injectRandomInt = field.getAnnotation(RandomInt.class);
            if (injectRandomInt != null) {
                int min = injectRandomInt.min();
                int max = injectRandomInt.max();
                int randomValue = randomIntGenerator.generate(min, max);
                field.setAccessible(true);
                ReflectionUtils.setField(field, bean, randomValue);
            }
        }
        return bean;
    }
}

2.2 示例测试

虽然代码可以正常编译运行,但启动 Spring 应用时,你会在日志中看到类似这样的警告信息:

INFO org.springframework.context.support.PostProcessorRegistrationDelegate$BeanPostProcessorChecker - Bean 'randomIntGenerator' of type [com.baeldung.autoproxying.RandomIntGenerator] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)

✅ 同时你会发现,DataCache 中的 group 字段并没有被正确注入:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {RandomIntProcessor.class, DataCache.class, RandomIntGenerator.class})
public class NotEligibleForAutoProxyingIntegrationTest {

    private RandomIntProcessor randomIntProcessor;

    @Autowired
    private DataCache dataCache;

    @Test
    public void givenAutowireInBeanPostProcessor_whenSpringContextInitialize_thenNotEligibleLogShouldShow() {
        assertEquals(0, dataCache.getGroup());
    }
}

不过好消息是,应用并不会因此崩溃,只是警告而已。

2.3 原因分析

这个警告的根源在于 RandomIntProcessor 类和它所依赖的 Bean(比如 RandomIntGenerator)之间形成了循环依赖。

Spring 在启动时,会优先实例化所有实现了 BeanPostProcessor 接口的类。而 AOP 自动代理机制本身也是通过 BeanPostProcessor 实现的。这就导致:

BeanPostProcessor 及其依赖的 Bean 不能参与 AOP 自动代理

在我们的例子中,虽然 DataCache 被成功注入到了 RandomIntGenerator 中,但由于 RandomIntProcessor 的提前加载,导致 @RandomInt 注解的处理失败。

3. 如何修复这个错误

要解决这个问题,我们需要 打破 BeanPostProcessor 和其依赖 Bean 之间的循环依赖

最简单粗暴的方法是:使用 Spring 的 @Lazy 注解延迟加载依赖项。

修改 RandomIntProcessor 的构造函数如下:

public class RandomIntProcessor implements BeanPostProcessor {
    private final RandomIntGenerator randomIntGenerator;

    @Lazy
    public RandomIntProcessor(RandomIntGenerator randomIntGenerator) {
        this.randomIntGenerator = randomIntGenerator;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        //...
    }
}

✅ 这样一来,RandomIntGenerator 就不会在 Spring 启动早期被强制初始化,而是在 postProcessBeforeInitialization 方法中首次调用时才加载。此时 Spring 容器已经完成了其他 Bean 的初始化,具备了完整的 AOP 代理能力。

重新运行应用,你会发现日志中不再出现 “not eligible for auto-proxying” 警告,同时 DataCachegroup 字段也被正确赋值:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {RandomIntProcessor.class, DataCache.class, RandomIntGenerator.class})
public class NotEligibleForAutoProxyingIntegrationTest {

    private RandomIntProcessor randomIntProcessor;

    @Autowired
    private DataCache dataCache;

    @Test
    public void givenAutowireInBeanPostProcessor_whenSpringContextInitialize_thenGroupFieldShouldBePopulated() {
        assertNotEquals(0, dataCache.getGroup());
    }
}

4. 总结

在这篇文章中,我们分析了 Spring 中 “not eligible for auto-proxying” 警告的常见成因,并通过一个实际的例子演示了如何通过 @Lazy 注解打破循环依赖,从而解决问题。

关键点总结如下:

  • ⚠️ BeanPostProcessor 的依赖 Bean 不能参与 AOP 代理
  • ✅ 使用 @Lazy 延迟加载依赖可以有效避免该问题
  • ❌ 警告不会导致应用崩溃,但可能影响某些注解功能的生效

完整代码示例可以在 GitHub 上找到。


原始标题:Solving Spring’s “not eligible for auto-proxying” Warning | Baeldung