记一种 spring 框架的想当然但错误的用法
Talk is cheap. Show me the code. —— Torvalds, Linus (2000-08-25).
The Case
废话不多说,直接上代码:
代码示例
示例代码到这里全部写完,大家猜测一下这个这个 test case 的结果会是什么?
Spring 容器加载过程
Spring 容器加载过程大体如下:
解析 xml 文件,获取 bean 定义
bean 定义的后置处理
bean 实例化的后置处理
在解析 xml 文件注册 BeanDefinition
的阶段,标签 context:component-scan
的使用有两个作用:
1. 扫描指定路径下被 @Component
, @Repository
, @Service
, and @Controller
注解的类,注册对应的 BeanDefinition
;
2. 通过将 @Required
, @Autowired
, @PostConstruct
, @PreDestroy
, @Resource
, @PersistenceContext
and @PersistenceUnit
注解对应的 BeanPostProcessor
实现类的 BeanDefinition
注册到 BeanDefinitionRegistry
(之后过程会实例化这些 BeanPostProcessor
),激活注解功能。
在 bean 定义的后置处理过程,主要是实例化 BeanFactoryPostProcessor
的实现类,并调用其中的 postProcessBeanFactory
方法,在实例化 BeanFactoryPostProcessor
的过程中,如果其存在依赖,也会遍历实例化其依赖,这和其他 bean 的实例化过程没有区别。
现在重点关注下被 @Autowired
或 @Resource
注解的属性的自动注入问题,这部分逻辑请查阅 关键代码 ,即获取当前 getBeanPostProcessorCache().instantiationAware
已经注册的 BeanPostProcessor
;AutowiredAnnotationBeanPostProcessor
和 CommonAnnotationBeanPostProcessor
分别处理 @Autowired
和 @Resource
注解的自动注入问题,都实现了 InstantiationAwareBeanPostProcessor
接口,先后执行 postProcessProperties
和 postProcessPropertyValues
获取需要注入的属性和要注入的值信息等。
回过头来看,getBeanPostProcessorCache().instantiationAware
中的 BeanPostProcessor
是什么时候注入进入的呢?查看 getBeanPostProcessors()
源码逻辑 ,是获取 beanPostProcessors
集合中成员,那何时往 beanPostProcessors
添加新的元素呢?存在两个入口 addBeanPostProcessor
和 addBeanPostProcessors
,在 源码 处调用,往上层看最开始在 源码 处开始注册 BeanPostProcessor
的逻辑。
在整体看下顺序,发现 BeanFactoryPostProcessor
实例化(invokeBeanFactoryPostProcessors
)时, BeanPostProcessor
还没有注册(registerBeanPostProcessors
),因此找不到 AutowiredAnnotationBeanPostProcessor
去处理 sampleBeanFactoryPostProcessor
中被 @Autowired
注解的属性,导致 businessService
属性无法注入。
写到最后
最后,我们再看下最开始抛出的那个问题的答案,当然是注入失败,断言不通过。
最初发现这个问题是,感觉这个是不是 Spring 的 bug,但是想到这么明显的问题,Spring 应该不会出现这种低级的犯错,于是在 Github 上 创建了 issue,得到了 Spring 开发者的回复:
This is by design:
BeanFactoryPostProcessor
is a very low-level callback and comes before any annotation processing (which is implemented in subsequentBeanPostProcessor
callbacks). Either wire up your post-processor explicitly (e.g. using<property>
) or use programmaticBeanFactory.getBean
calls within the post-processor implementation. Alternatively, consider putting your business-dependent logic into some other location;BeanFactoryPostProcessor
is really meant to be an infrastructure callback.
FWIW, a corresponding note can be found in our core beans chapter in the reference documentation.
大意是:有意设计成这样的,BeanFactoryPostProcessor
作为一种非常基础组件,在处理注解的逻辑之前发挥作用。如果我们一定需要在 BeanFactoryPostProcessor
中注入依赖,可以通过在 xml 显示配置 <property></property>
的方式 或 BeanFactory.getBean
获取依赖的 bean 实例。
原 issue 地址贴这里:framework#25754 。
版权声明: 本文为 InfoQ 作者【小明同学】的原创文章。
原文链接:【http://xie.infoq.cn/article/97501ac4e3500982502863015】。文章转载请联系作者。
评论