springboot Bean提前加载未被BeanPostProcessor增强处理导致aop注解切面失效及解决办法

前言

最近碰到个问题,公司项目中使用了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(后置处理器)类作用

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));
}
BeanPostProcessor类数组

其中,Bean被提前加载的info日志就是由BeanPostProcessorChecker类输出的。


BeanPostProcessor类数组

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


postProcessorNames

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...)

你可能感兴趣的:(springboot Bean提前加载未被BeanPostProcessor增强处理导致aop注解切面失效及解决办法)