在公司内,Spring基本都是首选的IOC框架,Spring也提供了很多扩展点让我们介入到容器的生命周期中,例如BeanFactoryPostProcessor、BeanPostProcessor等。今天就记录下BeanFactoryPostProcessor的一种不正确用法。
1. 约定
实例化:instantiation
初始化:initialization
2. BeanFactoryPostProcessor的作用
首先贴下BeanFactoryPostProcessor的源码吧
1 | public interface BeanFactoryPostProcessor { |
首先请一字一句通读一下doc,其中两句话非常重要:
- BeanFactoryPostProcessor允许使用者修改容器中的bean definitions
- BeanFactoryPostProcessor可以与bean definitions打交道,但是千万不要进行bean实例化(感觉这里应该说的是不要在BeanFactoryPostProcessor进行可能触发bean实例化的操作)。这么做可能会导致bean被提前实例化,会破坏容器造成预估不到的副作用。如果你需要hack到bean实例化过程,请考虑使用BeanPostProcessor。
从doc中可以读到,BeanFactoryPostProcessor的主要作用是让你能接触到bean definitions,对bean definitions进行一定hack,但是也仅此而已了。绝对不允许在BeanFactoryPostProcessor中触发到bean的实例化!!!
为啥呢,doc说得很清楚but never bean instances. Doing so may cause premature bean instantiation, violating the container and causing unintended side-effects.
下面就列举错误使用造成的两种典型“副作用”。
3. 副作用1——使用注解进行依赖注入失败
贴一下示例代码片段吧。
1 |
|
如上demo所示,在运行后,BBean中被期待注入的ABean最终为null。这是为啥呢?
贴一段ApplicationContext的启动代码吧
1 |
|
可以看到的是postProcessBeanFactory(beanFactory);
首先invoke了容器中的BeanFactoryPostProcessor实现类,其中当然就包括PrematureBeanFactoryPostProcessor,此时通过beanFactory.getBeansOfType
触发了bean提前实例化。按理说,bean提前实例化也应该没问题的,aBean也应该是能够被注入的呀!那为啥最终不是这个结果呢。让我们研究下@Resource @AutoWired这种注解是如何注入依赖的,如何起作用的就明白了。@AutoWired起作用依赖AutowiredAnnotationBeanPostProcessor,@Resource依赖CommonAnnotationBeanPostProcessor,这俩都是BeanPostProcessor的实现。那BeanPostProcessors在何处被spring invoke呢,参见registerBeanPostProcessors(beanFactory);
在postProcessBeanFactory(beanFactory);
后面被调用,也就是说BBean被触发提前初始化的时候,AutowiredAnnotationBeanPostProcessor还没有被注册自然也不会被执行到,自然ABean=null
4. 副作用2——可能会将ApplicationContext容器启动过程暴露在多线程之下
“将ApplicationContext容器启动过程暴露在多线程之下”倒不完全是因为错误使用BeanFactoryPostProcessor,而是错上加错。假设我们发现在副作用1的场景下aBean无法注入,而将BBean通过如下方式修改
1 | "bBean") ( |
这样问题就更大了,虽然在大多数的场景下不会有问题。但是如果BBean是类似于像DTS processer bean或者像是MetaQ的 listener bean情况就非常糟糕了。这种bean一旦被实例化+初始化之后就可以接受请求,如果在请求处理代码中触发了Spring bean的实例化过程,则此时的容器会被破坏,结果是无法预知的。而且问题和可能不太容易复现,因为只有在spring启动过程中请求进来触发ABean首次实例化才有可能会发生错误。强烈要求MetaQ listener DTSprocesser等bean依赖容器自身的依赖注入,不要认为干扰;如果实在要用listener or processor 中使用context.getBean(“aBean”)的方式,那也请做好listener or processor 的生命周期管理,保证在容器彻底起来之后才开始处理请求
5. 总结
本文列举了BeanFactoryPostProcessor错误使用可能造成的问题。认真读doc,共勉!
原文出自:https://www.jianshu.com/p/3d099ea43b0e
参考:https://www.cnblogs.com/quanyongan/p/4133725.html