1. 概述
在本簡短教程中,我們將學習如何追蹤 Spring 中“未符合自動代理資格”消息的原因,以及如何解決它。
首先,我們將創建一個簡單的實際應用代碼示例,該示例在應用程序啓動期間會導致該消息出現。 然後,我們將解釋為什麼會發生這種情況。
最後,我們將通過展示一個可工作的代碼示例,來提供該問題的解決方案。
2. “未符合自動代理”消息的原因
2.1. 示例配置
在解釋消息的原因之前,我們先構建一個示例,導致應用程序啓動期間出現該消息。
首先,我們將創建一個自定義 <em >RandomInt</em> 註解。我們將使用它來註解那些應在指定範圍內插入隨機整數的字段:
@Retention(RetentionPolicy.RUNTIME)
public @interface RandomInt {
int min();
int max();
}第二,讓我們創建一個 <em >DataCache</em> 類,它是一個簡單的 Spring 組件。我們希望將緩存分配給一個隨機組,用於支持分片等,為此,我們將使用自定義註解對該字段進行標註:
@Component
public class DataCache {
@RandomInt(min = 2, max = 10)
private int group;
private String name;
}現在,讓我們來查看 RandomIntGenerator 類。它是一個 Spring 組件,我們將使用它來將隨機 int 值插入到由 RandomInt 註解標記的字段中:
@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. 測試我們的示例
即使一切編譯成功,當我們運行 Spring 應用程序並查看其日誌時,我們將會看到 Spring 的 BeanPostProcessorChecker 類生成的 “未符合自動代理” 消息:
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. 查找原因
警告是由 <em>RandomIntProcessor</em> 類及其自動注入的依賴項引起的。實現 <em>BeanPostProcessor</em> 接口的類在 <em>ApplicationContext</em> 的特殊啓動階段實例化,在任何其他 Bean 初始化之前。
此外,AOP 自動代理機制也是一個 <em>BeanPostProcessor</em> 接口的實現。因此,<em>BeanPostProcessor</em> 實現以及它們引用的 Bean 都不能直接進行自動代理。這意味着使用 AOP 的 Spring 功能(如自動注入、安全或事務註解)在這些類中可能無法按預期工作。
在我們的例子中,我們能夠將 <em>DataCache</em> 實例自動注入到 <em>RandomIntGenerator</em> 類中,而無需任何問題。但是,組字段未填充隨機整數。
3. 如何解決錯誤
為了消除“未符合自動代理資格”消息,我們需要打破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 {
//...
}
}Spring 在 postProcessBeforeInitialization 方法中,當 RandomIntProcessor 請求它時,會初始化 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. 結論
在本文中,我們學習瞭如何追蹤並修復 Spring 中“未符合自動代理”消息的原因。懶加載在 Bean 構造過程中打破了依賴循環。