最近碰到个问题,公司项目中使用了shiro权限框架并自定义了UserRealm类,目前我要在其中注入一个Service类,调用运行一个业务逻辑,而这个方法上带有aop注解切面。结果,这整个Service类内的所有方法上带有的切面都失效了,就很牛逼,其他Service内同个注解的切面则正常。
然后我注意到在控制台上有条关于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本身也是一个Bean,一般而言实则例化时机要早过普通的Bean。
百度后我清楚了,是由于UserRealm在BeanPostProcessor之前加载了,没有被增强处理过导致的。
从Application运行开始 -> ServletWebServerApplicationContext类的refresh() -> 父类AbstractApplicationContext的refresh() -> registerBeanPostProcessors() -> PostProcessorRegistrationDelegate类的静态registerBeanPostProcessors()。
public static void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {
    // 注意:此处只会拿到Bean的定义信息~~~~
    // 已经被实例化的Bean最终都会调用`beanFactory.addBeanPostProcessor`而缓存在AbstractBeanFactory的字段:beanPostProcessors里,它是个CopyOnWriteArrayList
    // 更重大的是:最终最终所有的BeanPostProcessor的执行都会从这个List里面拿出来执行
    // 所以这一步很关键:那就是按照顺序,把`BeanPostProcessor`们都实例化好,然后添加进List里
    // 因此顺序是关键~~~~~如果某些Bean提前被实例化,它就很有可能不能被所有的`BeanPostProcessor`处理到了
    // 这也是我们BeanPostProcessorChecker的作用,它就是检查这个然后输出日志的~
    String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);
    // 这个beanProcessorTargetCount此处赋值了,后续就都不会变了,BeanPostProcessorChecker就是和这个进行比较的~
    // beanFactory里面的Bean实例总个数+1(自己)+bean定义信息~
    int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
    // 把BeanPostProcessorChecker加进去,它实则就是做了一个检查而已~~~~~~~输出一个info日志~
    beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));
    // 1、找到所有实现PriorityOrdered的`BeanPostProcessor`,然后getBean,然后统一排序,然后beanFactory.addBeanPostProcessor()
    // 2、处理实现Ordered的,步骤同上
    // 3、处理没实现排序接口的普通的处理器,不需要sort了,直接add进去~
    // 最后注册一个特殊的处理器
    beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
}
其中,Bean被提前加载的info日志就是由BeanPostProcessorChecker类输出的。

这是最后添加完毕的BeanPostProcessor类数组。

postProcessorNames数组里的就是早于BeanPostProcessor加载的类,UserRealm附带于红框内的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...)
 ¥42.00
 
                ¥42.00
            PC中文正版Steam游戏 帝国时代2决定版 Age of Empires II: Definitive Edition 帝国2帝国时代二 国区激活码
 ¥18.00
 
                ¥18.00
            Steam正版PC中文游戏 辐射76 Fallout 76 废土人 末日 钢铁黎明 国区激活码CDK
 ¥35.00
 
                ¥35.00
            PC中文正版steam 美国卡车模拟激活码 美卡 cdkey American Truck Simulator 在线联机国区全球 序列号cdkey
 ¥11.90
 
                ¥11.90
            PC中文正版 steam平台 国区 游戏 远方孤帆 FAR Lone Sails 孤帆远航 激活码
 ¥17.42
 
                ¥17.42
            PC中文正版 steam平台 国区 游戏 沙石镇时光 My Time at Sandrock 激活码 兑换码
 ¥9.00
 
                ¥9.00
            PC中文steam 掠食 Prey 国区礼物 国区CDKey激活码 动作 冒险