BeanPostProcessor
是Spring中参与Bean生命周期定制非常重要的一个手段,上文分析过,其执行有两个时机
- 一前:Bean自动注入之后,自定义初始化方法调用前
- 一后:自定义方法调用之后
Spring中很多重要的特性利用了BeanPostProcessor
达成,毕竟,算来算去,Spring中整个Bean的生命周期已经足够复杂了,如果每加一个功能就要在生命周期上做文章,只会增加复杂度,而BeanPostProcessor
则是Spring提供的一种扩展方式。与其相对的,一般用户用的可能较少的BeanFactoryPostProcessor
是针对整个容器初始化完成后提供定制化功能的扩展,我们也要观察一下。观察的主要内容是主要实现类及其作用。
BeanFactoryPostProcessor
它的调用,在org.springframework.context.support.AbstractApplicationContext#refresh.564行
。
其接口及其简单:一个简单的函数式接口。
1 |
|
这意味着,在容器创建后,我们能够向其中设置任何内容,也可以利用容器刚刚创建这个时机,来做一些时机上再容器全局的一些事情。
具体来说,有这么几类
修改现有容器配置
AbstractDependsOnBeanFactoryPostProcessor
、CacheManagerEntityManagerFactoryDependsOnPostProcessor
。强制为某些Bean显式设置依赖关系,使得不满足依赖时容器无法启动。这在自动配时会有用CustomScopeConfigurer
,添加自定义scope,这在WebSocketMessageBrokerConfigurationSupport
有使用CustomEditorConfigurer
,注册一些自定义的PropertyEditor
LazyInitializationBeanFactoryPostProcessor
,它将容器中没有指定延迟加载属性的bean定义,除以下条件的bean设置为延迟加载SmartInitializingSingleton
类型AopInfrastructureBean
类型TaskScheduler
类型ScheduledExecutorService
类型- 被
@Scheduled
或Schedules
注解的类
向容器中添加Bean
ServletComponentRegisteringPostProcessor
,它创建了一个Servlet环境,注册了必要的BeanEventListenerMethodProcessor
,它配合SmartInitializingSingleton
,实现了@EventListener
注解- 在容器初始化完成后,获取并持有了容器的
EventListenerFactory
、容器本身 - 在单例Bean初始化完成后,检测带有
@Component
的Bean内部是否有@EventListener
注解的方法,如果有,则使用上一步持有的EventListenerFactory
将该方法创建为一个ApplicationListener
实例,然后注入容器
- 在容器初始化完成后,获取并持有了容器的
一些全局开关
AspectJWeavingEnabler
,开启AspectJ。
BeanPostProcessor
它的执行时机,有两个:Bean创建后,实例化前;Bean实例化后。
1 | public interface BeanPostProcessor { |
下面是主要实现
xxxAware
ApplicationContextAwareProcessor
,在Bean初始化之前,调用了如下接口的setxxx方法EnvironmentAware
EmbeddedValueResolverAware
ResourceLoaderAware
ApplicationEventPublisherAware
MessageSourceAware
ApplicationContextAware
ApplicationStartupAware
BootstrapContextAwareProcessor
,在Bean初始化之前,调用了如下接口的setxxx方法BootstrapContextAware
WebApplicationContextServletContextAwareProcessor
,在Bean初始化之前,调用了如下接口的setxxx方法ServletContextAware
ServletConfigAware
ConfigurationPropertiesBindingPostProcessor
它将环境中的属性绑定到@ConfigurationProperties
注解到的类上。比如
1 |
|
它能够将配置中的如下属性注入对象
1 | aliyun.access-key=xxxx |
来看看该类的源码
1 | public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProcessor, PriorityOrdered, ApplicationContextAware, InitializingBean { |
理解它的关键,在于理解Spring的绑定机制,该机制有点复杂,不是一两句能说清的。简单来说,就是将一堆属性绑定到指定的领域模型。我们只简单地看一下上面涉及到的两个类。具体的,下一篇文章再来看。
ConfigurationPropertiesBean
1 | public final class ConfigurationPropertiesBean { |
- 对
Bindable
和BindMethod
,可能有一些陌生,暂且不管,后面专门写文章介绍它 ConfigurationPropertiesBean
中包含的内容:目标bean实例、ConfigurationProperties
注解、绑定目标、绑定方式- 该类为绑定做准备
ConfigurationPropertiesBinder
1 | class ConfigurationPropertiesBinder { |
- 最终还是委托给了
Binder
进行调用,ConfigurationPropertiesBinder
只能算是一个代理,准备好绑定需要的组件,然后调用Binder
完成绑定 - 我们看到大量的从容器中获取绑定组件的方式,却没看到什么时候在容器中创建了该bean?实际上
getBean()
方法的首次调用就完成了创建和返回两个操作 - 绑定包含了验证的过程,默认会使用两个验证器
ConfigurationPropertiesValidator
ConfigurationPropertiesJsr303Validator
- 支持JSR303验证规范的前提条件:同时存在如下三个类型的Bean
Validator
ValidatorFactory
GenericBootstrap
ApplicationListenerDetector
ApplicationListenerDetector
,用于检测实现了ApplicationListener
的Bean,并将其注入容器和时间广播器。
1 | class ApplicationListenerDetector implements DestructionAwareBeanPostProcessor, MergedBeanDefinitionPostProcessor { |
AutowiredAnnotationBeanPostProcessor
可以以该类为入口,查看整个Spring自动注入的逻辑。首先其继承结构如下。
这就意味着有以下几个扩展点可以观察
获取到
MergedBeanDefinition
之后的执行点(关于MergedBeanDefinition
在关于Bean的描述那篇文章讨论过,这里不再看)调用点:
AbstractAutowireCapableBeanFactory.java:1116行,applyMergedBeanDefinitionPostProcessors()方法
Bean的属性读取完成之后的执行点
调用点:
AbstractAutowireCapableBeanFactory.java:1436行,polupateBean()方法
构建Bean实例时用于判断构造方法参数的执行点
调用点:
AbstractAutowireCapableBeanFactory.java:1302行,determineConstructorsFromBeanPostProcessors()方法
实际上,该processor同时干预了字段注入、方法注入、构造器注入。
首先看其构造方法,可知,适用的注解有三个:@Autowired、@Value、@Inject
,最后一个是适配JSR330。
1 | public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor, MergedBeanDefinitionPostProcessor { |
然后看第一个扩展点:postProcessMergedBeanDefinition()
,与他相关的方法全部摆出来。
1 | public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) { |
- 三个注解
@Autowired、@Value、@Inject
,作用在字段或构造方法上都可以;甚至可以作用在静态字段和方法上,只不过会被忽略而已 - 作用在方法上时,该方法必须拥有参数,方法不应是setter方法
- 决定注入是否必须的条件:未指定required时默认为必须,否则指定什么就是什么
然后我们来看第二个扩展点:postProcessProperties()
,它是执行注入的地方
1 | public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) { |
- 看起来,自动注入的方法,只允许有一个参数。
最后来看它是如何决定构造方法以干预实例创建的:determineCandidateConstructors()
1 | public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, final String beanName) throws BeanCreationException { |
- 该方法做了两件事:处理
@Lookup
注解、处理候选构造方法。关于@Lookup
注解:https://www.baeldung.com/spring-lookup,它注解在方法上,容器会根据其返回值从容器中寻找对应的Bean
总结一下,AutowiredAnnotationBeanPostProcessor
做了以下事情
- 被
@Autowired、@Value、@Inject
注解的字段,会注入对应类型的实例;对被注解的方法,会将方法的参数注入对应类型的实例 - 被
@Lookup
注解的方法,返回值将不会去方法真是返回的,而是返回类型对应的容器内的Bean实例
RequiredAnnotationBeanPostProcessor
先说明:
@Required
注解已经过时了,现在推荐的使用方式是使用构造器注入,或实现自定义的InitializingBean
。
其关键逻辑比较简单:检查被@Required
注解的字段,是否都已经找到值准备注入,如果这个时候还没有值,就会报违反“必须”的错误。
1 | public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) { |
InitDestroyAnnotationBeanPostProcessor
它其实和AutowiredAnnotationBeanPostProcessor
类似,在BeanDefinition
准备好后查找@PostConstruct、@PreDestroy
,后在对应的时机调用查找好的方法。
首先是查找这些方法的点:postProcessMergedBeanDefinition()
1 | public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) { |
然后是@PostConstruct
的执行点:postProcessBeforeInitialization()
1 | public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { |
最后是@PreDestroy
的执行点:postProcessBeforeDestruction()
1 |
|
从这个源码看来
- 被
@PostConstruct、@PreDesctroy
注解的方法可以有多个,他们会依次执行 - 被它们注解的方法需要是无参的,就算有参,也不会给你传值
ScheduledAnnotationBeanPostProcessor
源码分析
大致描述一下它的工作原理:
- 在单例初始化完成后,或收到
ContextRefreshEvent
事件后,该Processor
需要准备好,准备的内容包括初始化用于指定定时任务的Scheduler
,持有任务元数据的ScheduledTaskRegistrar
; - 在实例初始化完成后,从容器中查找带有
@Scheduled、@Schedules
注解的方法,解析定时参数,构建成任务,然后提交执行; - 在单个实例销毁前,从缓存中删除该实例对应的定时任务
- 在容器销毁前,清空缓存的定时任务
然后,我们来看准备阶段和检测阶段的代码,结束阶段就忽略掉了。先是准备阶段
1 | public ScheduledAnnotationBeanPostProcessor() { |
然后来看检测阶段
1 | public Object postProcessAfterInitialization(Object bean, String beanName) { |
至此,Processor
的任务结束了,我们来看看这些任务的执行逻辑,以ScheduledTaskRegistrar.scheduleFixedRateTask()
为例:
1 | public ScheduledTask scheduleFixedRateTask(FixedRateTask task) { |
定时器如何生效
定时器默认不生效,当需要使用时,通过@EnableScheduling
引入,其最终结果是向容器中声明了ScheduledAnnotationBeanPostProcessor
实例。
1 | // EnableScheduling注解引入了SchedulingConfiguration配置类 |
其它
还有两个非常重要的内容:BeanValidationPostProcessor
、AOP相关处理器,涉及内容较多,将单独来看,这里忽略。
总结
本文介绍了通过BeanPostProcessor
实现的几个关键内容:配置绑定类、事件监听器的侦测、自动注入的实现、初始化和销毁注解的实现、定时任务的实现等。