本文共 6441 字,大约阅读时间需要 21 分钟。
写在前面
在前面,我们学习了一下Spring中的IOC、AOP以及声明式事务等最核心的知识点,而且我们还掌握了它们的注解驱动开发,不仅如此,我们还从源码的角度分析了一下它们里面的工作原理,相信看过我前面文章的同学,一定对此深有体会。
而从这一讲开始,我们便来学习一下Spring里面一些其他的扩展原理,希望大家通过这些原理的学习,对Spring里面的运行机制,包括其内部的工作原理,能有一个非常深刻的认识,这样的话,对于大家来学习Spring里面的其他框架会有非常好的帮助。
从源码角度理解BeanFactoryPostProcessor的原理
根据我们在第一讲中的安排,我们接下来要说的Spring里面的一些扩展原理有:
- BeanFactoryPostProcessor
- BeanDefinitionRegistryPostProcessor
- ApplicationListener
- Spring容器创建过程
不过在这一讲中,我们只是先来说一下BeanFactoryPostProcessor的原理,其他的在后续的文章中都会讲到。
BeanFactoryPostProcessor的调用时机
BeanFactoryPostProcessor其实就是BeanFactory(创建bean的工厂)的后置处理器。说起这个,你脑海中是不是泛起了回忆,是不是想起了有一个与BeanFactoryPostProcessor的名字极其相似的玩意,它就是BeanPostProcessor。那什么是BeanPostProcessor呢?我们之前早就说过了,它就是bean的后置处理器,并且是在bean创建对象初始化前后进行拦截工作的。
现在我们要讲解的是BeanFactoryPostProcessor,上面也说过了,它是BeanFactory(创建bean的工厂)的后置处理器。接下来,我们就要搞清楚它的内部原理了,想要搞清楚其内部原理,我们需要从它是什么时候工作这一点开始入手研究,也即搞清楚它的调用时机是什么。
我们点进去BeanFactoryPostProcessor的源码里面去看一看,发现它是一个接口,如下图所示。
仔细看一下其内部postProcessBeanFactory方法上的描述,这很重要,因为从这段描述中我们就可以知道BeanFactoryPostProcessor的调用时机。描述中说,我们可以在IOC容器里面的BeanFactory的标准初始化完成之后,修改IOC容器里面的这个BeanFactory。
也就是说,BeanFactoryPostProcessor的调用时机是在BeanFactory标准初始化之后,这样一来,我们就可以来定制和修改BeanFactory里面的一些内容了。那什么叫标准初始化呢?接着看描述,它说的是所有的bean定义已经被加载了,但是还没有bean被初始化。
说人话,就是BeanFactoryPostProcessor的调用时机是在BeanFactory标准初始化之后,这样一来,我们就可以来定制和修改BeanFactory里面的一些内容了,此时,所有的bean定义已经保存加载到BeanFactory中了,但是bean的实例还未创建。
案例实践
接下来,我们来编写一个案例来验证一下以上说的内容。
首先,我们来编写一个我们自己的BeanFactoryPostProcessor,例如MyBeanFactoryPostProcessor。要编写这样一个bean工厂的后置处理器,它得需要实现我们上面说的BeanFactoryPostProcessor接口,并且还得添加一个实现方法。由于BeanFactoryPostProcessor接口里面只声明了一个方法,即postProcessBeanFactory,所以咱们自己编写的MyBeanFactoryPostProcessor类中只需要实现其即可。
package com.meimeixia.ext;import java.util.Arrays;import org.springframework.beans.BeansException;import org.springframework.beans.factory.config.BeanFactoryPostProcessor;import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;import org.springframework.stereotype.Component;@Componentpublic class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { System.out.println("MyBeanFactoryPostProcessor...postProcessBeanFactory..."); // 这个时候我们所有的bean还没被创建 // 但是我们可以看一下通过Spring给我们传过来的这个beanFactory,我们能拿到什么 int count = beanFactory.getBeanDefinitionCount(); // 我们能拿到有几个bean定义 String[] names = beanFactory.getBeanDefinitionNames(); // 除此之外,我们还能拿到每一个bean定义的名字 System.out.println("当前BeanFactory中有" + count + "个Bean"); System.out.println(Arrays.asList(names)); }}
注意,我们自己编写的MyBeanFactoryPostProcessor类要想让Spring知道,并且还要能被使用起来,那么它一定就得被加在容器中,为此,我们可以在其上标注一个@Component注解。
然后,创建一个配置类,例如ExtConfig,记得还要在该配置类上使用@ComponentScan注解来配置包扫描哟!
package com.meimeixia.ext;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;/** * * @author liayun * */@ComponentScan("com.meimeixia.ext")@Configurationpublic class ExtConfig { }
当然了,我们也可以使用@Bean注解向容器中注入咱自己写的组件,例如,在这里,我们可以向容器中注入一个Blue组件。
package com.meimeixia.ext;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;import com.meimeixia.bean.Blue;/** * * @author liayun * */@ComponentScan("com.meimeixia.ext")@Configurationpublic class ExtConfig { @Bean public Blue blue() { return new Blue(); } }
上面这个Blue组件其实就是一个非常普通的组件,代码如下所示:
package com.meimeixia.bean;public class Blue { public Blue() { System.out.println("blue...constructor"); } public void init() { System.out.println("blue...init..."); } public void destory() { System.out.println("blue...destory..."); } }
可以看到,在创建Blue对象的时候,无参构造器会有相应打印。
接着,编写一个单元测试类,例如IOCTest_Ext,来进行测试。
package com.meimeixia.test;import org.junit.Test;import org.springframework.context.annotation.AnnotationConfigApplicationContext;import com.meimeixia.ext.ExtConfig;public class IOCTest_Ext { @Test public void test01() { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ExtConfig.class); // 关闭容器 applicationContext.close(); }}
测试啥呢?其实就是来验证一下BeanFactoryPostProcessor的调用时机。说得更具体一点就是,就是看一下咱们自己编写的BeanFactoryPostProcessor究竟是不是在所有的bean定义已经被加载,但是还未创建对象的时候工作?
那咱们接下来就来一探究竟。运行IOCTest_Ext类中的test01方法,可以看到Eclipse控制台打印出了如下内容。
哎呀!咱们自己编写的BeanFactoryPostProcessor在Blue类的无参构造器创建Blue对象之前就已经工作了。细心一点看的话,从bean的定义信息中还能看到Blue组件注册到容器中的名字,只是此刻还没创建对象,如下图所示。
说明BeanFactoryPostProcessor是在所有的bean定义信息都被加载之后才调用的。
源码分析
接下来,我们就以debug的方式来看一下BeanFactoryPostProcessor的调用时机。
首先,在咱们自己编写的MyBeanFactoryPostProcessor类里面的postProcessBeanFactory方法处打上一个断点,如下图所示。
然后,以debug的方式来运行IOCTest_Ext类中的test01方法,如下图所示,程序现在停到了MyBeanFactoryPostProcessor类里面的postProcessBeanFactory方法处。
那么程序是怎么运行到这儿的呢?我们不妨从IOCTest_Ext类中的test01方法开始,来梳理一遍整个流程。
鼠标单击Eclipse左上角方法调用栈中的IOCTest_Ext.test01() line:12
,这时程序来到了IOCTest_Ext类的test01方法中,如下图所示。
可以看到现在是要来创建IOC容器的。
继续跟进代码,可以看到创建IOC容器时,最后还得刷新容器,如下图所示。
继续跟进代码,可以看到在刷新容器的过程中,还得执行在容器中注册的BeanFactoryPostProcessor(BeanFactory的后置处理器)的方法。
那具体是怎么来执行BeanFactoryPostProcessor的呢?我们继续跟进代码,发现又调用了一个invokeBeanFactoryPostProcessors方法,如下图所示。
继续跟进代码,可以看到又调用了如下一个invokeBeanFactoryPostProcessors方法。
跟进程序到这里,你有没有想过这样一个问题,此时要执行哪些BeanFactoryPostProcessor呢?从以上invokeBeanFactoryPostProcessors方法的参数中,我们可以看到第一个参数代表的是一个List<BeanFactoryPostProcessor>
集合,它里面保存的就是那些要执行的BeanFactoryPostProcessor。
也就是说现在要执行的BeanFactoryPostProcessor从名为nonOrderedPostProcessors的List<BeanFactoryPostProcessor>
集合中拿就可以了。
下面我们来仔细分析一下PostProcessorRegistrationDelegate类中的invokeBeanFactoryPostProcessors方法具体都做了哪些操作。
首先,来看一下如下图所示的这行代码,这行代码说的是拿到所有BeanFactoryPostProcessor组件的名字。
然后,来挨个看相应名字的BeanFactoryPostProcessor组件,哪些是实现了PriorityOrdered接口的,哪些是实现了Ordered接口的,以及哪些是什么接口都没有实现的,说的简单一点就是将不同的BeanFactoryPostProcessor组件给分离出来。
接着,分别按不同的执行顺序来处理三种不同的BeanFactoryPostProcessor组件。
由于咱们自己编写的BeanFactoryPostProcessor既没有实现PriorityOrdered接口,也没有实现Ordered接口,所以就按照最后一种顺序来执行。
以上就是对PostProcessorRegistrationDelegate类中的invokeBeanFactoryPostProcessors方法大致分析。分析完之后,我们继续跟进代码,会发现其遍历了所有的BeanFactoryPostProcessor组件,我们自己编写的实现了BeanFactoryPostProcessor接口的MyBeanFactoryPostProcessor类肯定也属于其中,所以会被遍历到,然后便会执行其postProcessBeanFactory方法。
以上就是我们从源码的角度分析了一下BeanFactoryPostProcessor的整个执行顺序以及原理。
小结
经过以上源码分析,我们可以得出这样一个简单结论:首先从IOC容器中找到所有类型是BeanFactoryPostProcessor的组件,然后再来执行它们其中的方法,而且是在初始化创建其他组件前面执行。
我为什么可以这么肯定地说呢?如果我们大家还有回忆的话,那么你一定记得在我讲解AOP原理的时候,bean的创建与初始化还在很后面,如下图所示。
转载地址:https://liayun.blog.csdn.net/article/details/113103870 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!