51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

解决 Spring 中 “not eligible for auto-proxying” 警告

1、概览 {#1概览}

本文将带你了解出现 "not eligible for auto-proxying" 警告的原因以及如何修复它。

2、"not eligible for auto proxying" 的原因 {#2not-eligible-for-auto-proxying-的原因}

2.1、配置示例 {#21配置示例}

首先,创建一个自定义 RandomInt 注解,使用它来注解应该插入指定范围内的随机整数的字段。

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

}

其次,创建一个简单的 Spring 组件 DataCache 类。将一个可能被使用的随机 Group 分配给缓存,例如用于支持分片。为了实现这一点,使用自定义的注解来注解该字段:

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

现在,来看看 RandomIntGenerator 类。它是一个 Spring 组件,用于在 RandomInt 注解注解的字段中插入随机 int 值:

@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 类,它将负责查找注解为 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; }

}

它使用 org.springframework.beans.factory.config.BeanPostProcessor 接口的实现,在类初始化之前访问注解字段。

2.2、测试示例 {#22测试示例}

尽管编译一切正常,但当运行 Spring 应用并查看其日志时,会发现 Spring 的 BeanPostProcessorChecker 类生成了一条 "not eligible for auto proxying"的消息:

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 Bean 并没有按照预期进行初始化:

@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、原因分析 {#23原因分析}

该警告由 RandomIntProcessor 类及其自动装配的依赖引起。作为 ApplicationContext 特殊启动阶段的一部分,实现 BeanPostProcessor 接口的类会在启动时实例化,比其他 Bean 更早。

此外,AOP 自动代理机制也是 BeanPostProcessor 接口的实现。因此,BeanPostProcessor 实现和它们直接引用的 Bean 都不符合自动代理的条件。这意味着,Spring 使用 AOP 的功能(如自动装配、Security 或 Transactional 注解)将无法在这些类中正常工作。

在本例中,可以将 DataCache 实例自动装配到 RandomIntGenerator 类,不会出现任何问题。但是,group 字段没有填充随机整数。

3、修复这个问题 {#3修复这个问题}

为了消除 "not eligible for auto proxying" 警告,需要打破 BeanPostProcessor 实现与其 Bean 依赖之间的循环。在本例中,需要告诉 IoC 容器以懒加载的方式初始化 RandomIntGenerator Bean。

可以使用 Spring 的 @Lazy 注解:

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 { //... }

}

RandomIntProcessorpostProcessBeforeInitialization 方法中请求 RandomIntGenerator Bean 时,Spring 就会初始化 RandomIntGenerator Bean。此时,Spring 的 IoC 容器会实例化所有符合自动代理条件的现有 Bean。

现在,运行应用,就不会在日志中看到 "not eligible for auto proxying" 警告。更重要的是,DataCache Bean 的 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_thenGroupFieldShouldBePopulated() { assertNotEquals(0, dataCache.getGroup()); }

}

4、总结 {#4总结}

本文介绍了 Spring 中出现 "not eligible for auto-proxying" 警告的原因,以及如何通过 @Lazy 注解以延迟初始化的方法来解决这个问题。


Ref:https://www.baeldung.com/spring-not-eligible-for-auto-proxying

赞(2)
未经允许不得转载:工具盒子 » 解决 Spring 中 “not eligible for auto-proxying” 警告