Spring注解09——BeanPostProcessor后置处理器深度剖析-爱代码爱编程
该系列文章主要学习 雷丰阳老师的《Spring注解驱动》课程总结。
原课程地址:课程地址包括了自己阅读其他书籍《Spring揭秘》《Spring Boot 实战》等课程。
该系列文档会不断的完善,欢迎大家留言及提意见。
1. 写在之前
本篇会有一些源码的分析,如果暂时觉得没有用处,可以跳过该章节。
为什么要有 BeanPostProcessor 呢?
想要插手容器的管理过程,在实例化对象之前,对注册到容器中的 Bean 的基本信息做一些修改。相当于在容器进行第一道程序之前做一些额外的工作。
2. BeanPostProcessor 是什么
首先查看源码中的定义,主要是在 Bean 的实例化前后加上一些自己的业务逻辑,如果有多个 BeanPostProcessor 怎么办呢?
需要让 该实现类再实现 org.springframework.core.Ordered 接口,定义自己的顺序,会按照它们在容器中注册的顺序执行。
public interface BeanPostProcessor {
/**
* 在 bean实例化之前通过该方法加入自己的 私货
*/
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
/**
* bean 实例化之后再加上自己的 私货
*/
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
Spring 本身已经提供了现成的 BeanPostProcessor 实现类,如果没有特殊需求我们一般很少自定义 BeanPostProcessor。
CommonAnnotationBeanPostProcessor
AsyncAnnotationBeanPostProcessor
因此今天我们先来分析一下 Spring 给我提供的 Bean 是如何工作的。
3. xxxBeanPostProcessor 源码分析
先自定义一个 MyBeanPostProcessor ,该定义没有任何功能,只是输出了一些日志。
/**
* 后置处理器,在初始化前后进行处理工作
*/
@Component
public class MyBeanPostProcessor implements BeanPostProcessor, Ordered {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessBeforeInitialization..." + beanName + "=>" + bean);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessAfterInitialization..." + beanName + "=>" + bean);
return bean;
}
@Override
public int getOrder() {
return 3;
}
}
我们可以通过debug 模式反着查看,postProcessBeforeInitialization() 方法是在什么时候被触发的。
了解了整个调用过程中之后再来给大家分析 , BeanPostProcessor何时被调用,已经调用的过程。
第一步:main方法入口加载配置类。
这一步没有什么可说的,只是创建了一个 AnnotationConfigApplicationContext 对象。并且通过构造器来触发容器的初始化过程。
public class MainTest {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(PersonConfig.class);
System.out.println("applicatiomContext: " + applicationContext);
}
}
第二步:通过 refresh()刷新容器。
public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
this();
register(annotatedClasses);
// 这一步最为关键
refresh();
}
第三步:通过 refresh() 方法来初始化 Bean 对象。
该方法特别长,此处我们只是抓住我们要分析的重点,直接查看初始化单例模式的组件
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//省略其他步骤
// 初始化单例的组件
finishBeanFactoryInitialization(beanFactory);
}
// 暂时先忽略其他步骤
}
第四步:完成Bean 工厂环境的初始化,开始初始化所有的单实例 bean 组件。
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// spring context的一些设置,此处同样先忽略
// Instantiate all remaining (non-lazy-init) singletons.
// 这里和三步一样,对于我们此次要分析的还是啥都没干。
beanFactory.preInstantiateSingletons();
}
第五步:跳过一些暂时不相关的步骤来到初始化Bean的过程。
后续的一连串操作只是为了从容器的一大堆 Bean中找到每个Bean ,并且执行初始化方法,所以我们沿着堆栈直接跳到 Bean的初始化方法中。
仔细看初始化 Bean 的方法
protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
// 1. security的一些校验,与此次分析无关,可以先忽略
if (System.getSecurityManager() != null) {
AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override
public Object run() {
invokeAwareMethods(beanName, bean);
return null;
}
}, getAccessControlContext());
}
else {
invokeAwareMethods(beanName, bean);
}
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null),
beanName, "Invocation of init method failed", ex);
}
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
其实上面这堆代码主要看这一行,翻译过来就是:在初始化之前先调用 BeanPostProcessors 方法。
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
第六步:真正的调用 BeanPostProcessors 方法。通过该方法来调用我们自定义的和系统中自带的 BeanPostProcessors 实现类。
@Override
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
result = beanProcessor.postProcessBeforeInitialization(result, beanName);
if (result == null) {
return result;
}
}
return result;
}
可以看到我们自定义的 MyBeanPostProcessors 就在其中,Spring 这里利用了增强 for 循环,一次执行每一个 BeanPostProcessors 实现类。
也正是在这里完成了我们自定义 MyBeanPostProcessors 的 postProcessBeforeInitialization()方法调用,另一个的方法 postProcessAfterInitialization 实现的逻辑类似
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p47yDhE6-1629010353889)(/Users/xiaxiaojian/Library/Application%20Support/typora-user-images/image-20210814200753321.png)]
4. Spring框架用该接口干嘛呢?
刚才 debug 过程中,我们发现了 Spring 本身自带了很多 BeanPostProcessors 实现类,我们分析一个类,看看Spring 到底用这个功能干什么。
以 ApplicationContextAwareProcessor 实现类为例,该实现类本身只是用到了 postProcessBeforeInitialization() 方法,具体的实现功能如下。
方法看起来很长,不过大部分代码跟我们这次分析无关,最关键的是 invokeAwareInterfaces()方法
核心功能如下
@Override
public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
// 先忽略 security 验证
// 其实就是调用各种 Aware 接口
invokeAwareInterfaces(bean);
}
return bean;
}
关于 invokeAwareInterfaces() 主要逻辑如下,其实就是将各种依赖与bean 组件进行绑定。
关于 各种各样的 Aware 后续有机会给大家介绍,这里先简单说明功能
将 Aware 接口定义中规定的依赖注入给当前实例对象。
private void invokeAwareInterfaces(Object bean) {
if (bean instanceof Aware) {
if (bean instanceof EnvironmentAware) {
((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
}
if (bean instanceof EmbeddedValueResolverAware) {
((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
}
if (bean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
}
// 事件发布
if (bean instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
}
if (bean instanceof MessageSourceAware) {
((MessageSourceAware) bean).setMessageSource(this.applicationContext);
}
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
}
}
}
总结
整个 Spring 源码代码量比较大,你想一下 ,Spring已经迭代了多少年了,至少有几十万行(保守估计)。大家可以将复杂的源码拆解,每次一点点的分析。
分析源码目的不是为了看源码而看,而是知道这个功能是什么,是如何实现的,能否有借鉴的思想。
其实 BeanPostProcessors 说白了,定义了一个接口,有众多的实现类,在每次初始化 Bean 之前,依次调用每个实现类的方法,循环调用哦。
在 Spring 中有大量的这种设计模式,循环调用,这也侧面证明了为啥Spring 启动如此慢了。