Spring源码分析之BeanPostProcessor
Bean定义后置处理器(一)–BeanPostProcessor
1.、 应用场景 ,Bean定义在初始化的时候,我们可以偷偷的做一些事情,比如修改bean的一些信息等;
Spring组件在创建的时候,都会方法回调,既能获取这个组件而且我们可以做一些初始化数据的工作。
2、 源码分析
// BeanPostProcessor 接口如下
public interface BeanPostProcessor {
//第一个接口
postProcessBeforeInitialization(Object bean, String beanName);
//第二个接口
postProcessAfterInitialization(Object bean, String beanName);
}
截图:
发现该接口被重写,那意味着不同的场景,子类有自己的实现逻辑,如下大量的子类继承这个接口,肯定重写以上两个方法。
2、 调用时机
对于调用时机来说,也是我们学习Spring最大的收获,因为知道了调用时机,我们可以很好的利用这个时机,完成我们特定的业务需求,即场景的实现。分析这块,不妨先从Spring IOC源码进行解读。
首先看一段代码:
@Component
public class MyFactoryBean implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessBeforeInitialization"+"方法执行了,BeanName为:"+beanName);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessAfterInitialization"+"方法执行了,beanName为:"+beanName);
return bean;
}
}
配置类如下,扫描是可以扫描到上面的Bean组件
@Configuration
@ComponentScan("com.xxsc.cn.testf")
public class MyConfig {
}
运行代码
public class AppTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
}
}
打印结果:
postProcessBeforeInitialization方法执行了,BeanName为:myConfig
postProcessAfterInitialization方法执行了,beanName为:myConfig
那么可以断定,在Spring容器启动的时候,上面这两个方法执行了,根据BeanName的名字,进一步可断定Spring IOC在创建myConfig组件的时候,触发的,myConfig组件确实我们的配置类。
接下来,需要在关键节点打断点,先根据代码运行情况,分析在何时进行回调。
断点一:
AbstractApplicationContext 类 的 invokeBeanFactoryPostProcessors(beanFactory);方法,这个方法大概解释一下:Spring进行bean定义,并把Bean定义加载到容器当中,(bean定义是对Bean的描述,此时的bean没有初始化,更没有赋值)
断点二:
AbstractAutowireCapableBeanFactory 类的 doCreateBean方法的instanceWrapper = createBeanInstance(beanName, mbd, args);这一行
对于这一行的代码,在这之前,扫描完成的bean定义集合进行遍历,对bean定义进行初始化,也就是通过反射的技术创建对象,对于之前的分析,此时我们关注的应该是beanName为myConfig,断点条件设置为beanName.equals("myConfig")
断点三:
AbstractAutowireCapableBeanFactory 类的 doCreateBean方法的populateBean(beanName, mbd, instanceWrapper);这一行
对于这一行的代码,是对bean进行赋值,简单理解:断点二处,为new的操作(默认构造函数,如果是有参构造,此时断点二就开始赋值了),new完对象,断点三进行set赋值操作,因为spring依赖的属性,在创建bean的时候,就已经赋值,也就是DI注入了。此时对于断点同样设置条件:beanName.equals("myConfig")
断点四:
紧接着断点三的下一行代码,exposedObject = initializeBean(beanName, exposedObject, mbd);这行代码可以很NB了,spring提供了大量的开放式接口,用于我们去重写使用,对于这行代码,赋值完成之后,spring提供开放式接口,可以对bean一些修改,或者是获取spring中关系的信息,后面会写到。
DUBUG运行:
断点一并没有触发,断点二对bean进行初始化,发现也是没有回调。
断点三对于bean赋值完成之后,发现还是没有回调。
走完断点三,发现回调了,那么此时,执行时机来了: 配置类在初始化完成并且对配置类完成赋值之后,对于实现BeanPostProcessor的bean进行回调,那么对不对呢,需要来分析一下initializeBean这个方法到底做了写什么事,为什么会在这个地方进行方法的回调。
Debug进入到initializeBean方法的if (mbd == null || !mbd.isSynthetic()) { 这行代码,
再次进入到applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);这行代码
有个for循环,猜测找出BeanPostProcessor类型的子类,进行方法回调。