前言
最近碰到個問題宾娜,公司項目中使用了shiro權限框架并自定義了UserRealm類侈百,現(xiàn)在我要在其中注入一個Service類缀旁,調用運行一個業(yè)務邏輯更扁,而這個方法上帶有aop注解切面盖腕。結果,這整個Service類內(nèi)的所有方法上帶有的切面都失效了浓镜,就很牛逼溃列,其他Service內(nèi)同個注解的切面則正常。
探索過程
然后我注意到在控制臺上有條關于UserRealm類的info日志膛薛,如下:
2020-11-20 17:09:59.856 INFO 11872 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'userRealm' of type [xxx.shiro.UserRealm] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
翻譯結果是:
類型為[xxx.shiro.UserRealm]的Bean'userRealm'不符合所有BeanPostProcessor的處理要求(例如:不符合自動代理的條件)
而且基本都是shiro模塊相關的類輸出的听隐,之前沒了解過BeanPostProcessor類,但大概意思可以理解哄啄,代理未生效雅任。
切面失效原因及BeanPostProcessor(后置處理器)類作用
BeanPostProcessor本身也是一個Bean,一般而言其實例化時機要早過普通的Bean咨跌。
百度后我明白了沪么,是因為UserRealm在BeanPostProcessor之前加載了,沒有被增強處理過導致的锌半。
執(zhí)行過程
從Application運行開始 -> ServletWebServerApplicationContext類的refresh() -> 父類AbstractApplicationContext的refresh() -> registerBeanPostProcessors() -> PostProcessorRegistrationDelegate類的靜態(tài)registerBeanPostProcessors()禽车。
public static void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {
// 注意:此處只會拿到Bean的定義信息~~~~
// 已經(jīng)被實例化的Bean最終都會調用`beanFactory.addBeanPostProcessor`而緩存在AbstractBeanFactory的字段:beanPostProcessors里,它是個CopyOnWriteArrayList
// 更重要的是:最終最終所有的BeanPostProcessor的執(zhí)行都會從這個List里面拿出來執(zhí)行
// 所以這一步很關鍵:那就是按照順序刊殉,把`BeanPostProcessor`們都實例化好哭当,然后添加進List里
// 因此順序是關鍵~~~~~如果某些Bean提前被實例化,它就很有可能不能被所有的`BeanPostProcessor`處理到了
// 這也是我們BeanPostProcessorChecker的作用冗澈,它就是檢查這個然后輸出日志的~
String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);
// 這個beanProcessorTargetCount此處賦值了钦勘,后續(xù)就都不會變了,BeanPostProcessorChecker就是和這個進行比較的~
// beanFactory里面的Bean實例總個數(shù)+1(自己)+bean定義信息~
int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
// 把BeanPostProcessorChecker加進去亚亲,它其實就是做了一個檢查而已~~~~~~~輸出一個info日志~
beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));
// 1彻采、找到所有實現(xiàn)PriorityOrdered的`BeanPostProcessor`,然后getBean捌归,然后統(tǒng)一排序肛响,然后beanFactory.addBeanPostProcessor()
// 2、處理實現(xiàn)Ordered的惜索,步驟同上
// 3特笋、處理沒實現(xiàn)排序接口的普通的處理器,不需要sort了巾兆,直接add進去~
// 最后注冊一個特殊的處理器
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
}
其中猎物,Bean被提前加載的info日志就是由BeanPostProcessorChecker類輸出的虎囚。
這是最后添加完畢的BeanPostProcessor類數(shù)組。
postProcessorNames數(shù)組里的就是早于BeanPostProcessor加載的類蔫磨,UserRealm附帶于紅框內(nèi)的Bean淘讥。
解決方案
shiro模塊的Bean被提前加載是無法解決的,只能延遲其中注入的Service加載時間堤如。
@Autowired
@Lazy
private TestService testService;
如上蒲列,附上Spring的@Lazy注解就行了,或者使用@Autowired的required = "false"搀罢,然后在調用處主動校驗并從上下文獲取Service實例蝗岖。很明顯用@Lazy方便多了。
參考鏈接
【小家Spring】注意BeanPostProcessor啟動時對依賴Bean的“誤傷”陷阱(is not eligible for getting processed by all...)