文章目录
- 前言
- 一、后置处理器是什么?
- BeanFactoryPostProcessor
- BeanPostProcessor
- 二、后置处理器是如何工作的
- BeanFactoryPostProcessor
- Spring是如何使用的
- 第三方整合Sring
- BeanPostProcessor
- Spring是如何使用的
- 总结
前言
近日在重读Spring源码时,忽然有种打通任督二脉的感觉,有点慢慢摸到了Spring的设计精妙之处。后置处理器,相信对Spring稍有深入一些了解的同学多少都会知道。但是你知道为什么Spring要设计这个?这个东西的作用是什么?在整个IOC容器,甚至在整个Spring王国中的意义是什么?而这设计思路的背后,反映的是什么设计思想?
这里先卖个关子,本文中我会提出自己的拙见,欢迎大佬指点。此处只是想提醒下大家,在阅读的时候,应当带着目的。而且尽量多读几遍,因为第一次读的时候,很多东西无法联系起来思考和串联。
一、后置处理器是什么?
后置处理器分两大类:
一是:BeanFactoryPostProcessor
一是:BeanPostProcessor。
BeanFactoryPostProcessor
先看看官方解释:
/**
* Factory hook that allows for custom modification of an application context's
* bean definitions, adapting the bean property values of the context's underlying
* bean factory.
大意就是:允许自定义修改应用程序的上下文bean定义,调整BeanFactory的属性配置,的上下文的工厂钩子。
可能有点绕,意思是说,这是上下文bean工厂的工厂钩子。什么意思呢?模板模式大家知道吧?模板模式里面钩子方法有了解吧。这个东西的可以类比理解为在上下文的bean工厂的模式里钩子方法。
那么,这个“钩子方法”可以干什么呢?
- 允许自定义修改应用程序的上下文bean定义
- 修改BeanFactory的属性配置
BeanPostProcessor
同样的,我们看看官方注释
* Factory hook that allows for custom modification of new bean instances —
* for example, checking for marker interfaces or wrapping beans with proxies.
大意是:允许自定义修改新bean实例的工厂钩子,例如,检查标记接口或用代理包装bean。
这也是个“工厂钩子”,但与上面说的不一样,修改的是bean实例。
二、后置处理器是如何工作的
在回答这个问题之前,我们先来看看Bean的生命周期,我把Bean的声明周期分为4个阶段:
- Bean定义阶段:通俗点就是怀上了
- Bean实例化阶段
- Bean初始化阶段
- Bean销毁阶段
而处于不同目的/作用设计的后置处理器,工作在不同的阶段。
BeanFactoryPostProcessor
为了修改工厂中的Bean定义的BeanFactoryPostProcessor,工作在Bean定义阶段。注意,这里说的修改工厂中的Bean定义,不是修改某一个对象的bean定义,而是修改工厂中的bean定义。例如:增加bean定义-向容器中声明一个对象。这也是他与BeanPostProcessor的能力的重大区别之一。
Spring是如何使用的
以AnnotationConfigServletWebApplicationContext为例,Spring在上下文构建时,在构造器中创建AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner,以不同的方式扫描注册配置类,并且都会向容器中注入真正完成我们自定义的Bean的扫描和注册的BeanFactoryPostProcessor,以及功能强大的Bean修改和调用操作的BeanPostProcessor!
这里重点认识一下:ConfigurationClassPostProcessor。他就是幕后英雄,扫描所有的@Component(包括@Configuration),并注册到上下文。
第三方整合Sring
BeanFactoryPostProcessor对于第三方整合Spring的实际意义,主要是自定义注解来声明Bean。这个与Spring自己的ConfigurationClassPostProcessor是类似的思路。
以Dubbo的整合为例:
Dubbo对其自定的注解就是通过他自己定义的BeanFactoryPostProcessorcom.alibaba.dubbo.config.spring.AnnotationBean,来完成的(当然,这个也是BeanPostProcessor,只不过这里的关注点不是他)
对于第三方整合而言,还有另外一个选择:@Import。这个注解能力也很强悍。导入一份配置,常用的@Configuration就不说了,还有ImportSelector、ImportBeanDefinitionRegistrar。同样也可以向容器中添加bean定义。值得一提的是,他是由ConfigurationClassPostProcessor来调用处理的。你也可以把他当成这个后置处理器的钩子。似乎更多的第三方整合方案偏好这个方案。或许因为它可以避免与Spring自己的后置处理器的执行顺序问题吧,使用一把小钩子就行了。
BeanPostProcessor
与BeanFactoryPostProcessor不同,BeanPostProcessor工作在bean的初始化阶段。在这个阶段,Bean已经创建完成,并且属性已经注入了。他的两个接口方法,分别在初始化之前和初始化之后执行。
我在官网上找到的一张关于Bean的生命周期的图。
在完成各种Aware的设置之后,先执行了BeanPostProcessor的postProcessBeforeInitialization方法,即,初始化前。而在调用初始化方法之后,调用postProcessAfterInitialization方法。
Spring是如何使用的
前面说到上下文注册了BeanFactoryPostProcessor和BeanPostProcessor,那么都有谁呢?
- AutowiredAnnotationBeanPostProcessor:自动属性注入的,工作在实例化之后,将上下文找到可能的属性设置到bean中。
- CommonAnnotationBeanPostProcessor:处理@PostConstruct等注解的方法调用
- PersistenceAnnotationBeanPostProcessor:处理JPA的@PersistenceUnit等注解的
到这里,你可能会好奇,代理是通过什么织入的?代理不是IOC容器的默认功能,是需要我们自己引入aop的相关依赖并且开启注解的。
但还是告诉大家,代理也是通过BeanPostProcessor来实现的。他就是AnnotationAwareAspectJAutoProxyCreator。而他实现了Ordered接口,确保是最后一个执行的,避免重复织入代理。
对于我们自己使用的话,可以参考Spring的用法,定义注解/接口,在后置处理器里面调用。又或者,修改bean的属性值。等等。
总结
我们看源码重点之一是学习人家的设计思路。Spring这套的设计,体现了众多编码原则,例如:
- 开闭原则:通过各种各级钩子,对外/为将来提供拓展的可能,而不需要修改本身的类。
- 单一职责:通过定义各司其职的处理器,不要给我越权了。各自职责泾渭分明,边界明确。
- 接口隔离原则:对处理器进行拆解,抽象出来BeanFactoryPostProcessor和BeanPostProcessor,并界定边界。这个在ApplicationContext接口中的体现尤为明显。
但我最佩服的是Spring能够把如此庞大的一个设计,整的如此整体而统一,在保证拓展性的同时,还能如此井然有序,不带一点凌乱。确实牛啊!