记一种 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:
BeanFactoryPostProcessoris a very low-level callback and comes before any annotation processing (which is implemented in subsequentBeanPostProcessorcallbacks). Either wire up your post-processor explicitly (e.g. using<property>) or use programmaticBeanFactory.getBeancalls within the post-processor implementation. Alternatively, consider putting your business-dependent logic into some other location;BeanFactoryPostProcessoris 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】。文章转载请联系作者。
 
  
  
  
  
  
  
  
  
  
    
评论