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 {
//...
}
}
当 RandomIntProcessor
在 postProcessBeforeInitialization
方法中请求 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