Spring注解驱动开发第37讲——你知道Spring中BeanDefinitionRegistryPostProcessor是如何执行的吗?
发布日期:2021-06-30 17:56:23 浏览次数:4 分类:技术文章

本文共 7740 字,大约阅读时间需要 25 分钟。

写在前面

在上一讲中,我们学习了一下BeanFactoryPostProcessor接口,了解了一下它是怎样使用的,以及其内部原理,我们知道,BeanFactoryPostProcessor的调用时机是在BeanFactory标准初始化之后,这样一来,我们就可以来定制和修改BeanFactory里面的一些内容了。

接下来,我们就要学习一下BeanFactoryPostProcessor的一个子接口,即BeanDefinitionRegistryPostProcessor。

在这里插入图片描述

从上图中可以看到BeanDefinitionRegistryPostProcessor是BeanFactoryPostProcessor旗下的一个子接口。

从源码角度理解BeanDefinitionRegistryPostProcessor的原理

初步认识一下BeanDefinitionRegistryPostProcessor

首先,咱们来看一下BeanDefinitionRegistryPostProcessor的源码,如下图所示。

在这里插入图片描述

从该接口的名字中,我们大概能知道个一二,说它是bean定义注册中心的后置处理器并不过分。而且,从该接口的源码中我们也可以看出,它是BeanFactoryPostProcessor旗下的一个子接口。

我们还能看到,它里面定义了一个方法,叫postProcessBeanDefinitionRegistry,那么问题来了,它是什么时候执行的呢?我们可以看一下它上面的详细描述,说的是啥呢,说的是在IOC容器标准初始化之后,允许我们来修改IOC容器里面的bean定义注册中心。此时,所有合法的bean定义将要被加载,但是这些bean还没有初始化完成。

说人话就是,postProcessBeanDefinitionRegistry方法的执行时机是在所有bean定义信息将要被加载,但是bean实例还未创建的时候。 这句话听起来,总感觉BeanDefinitionRegistryPostProcessor是在BeanFactoryPostProcessor前面执行的,真的是这样吗?确实是这样。为什么呢?BeanFactoryPostProcessor的执行时机是在所有的bean定义信息已经保存加载到BeanFactory中之后,而BeanDefinitionRegistryPostProcessor却是在所有的bean定义信息将要被加载的时候,所以,BeanDefinitionRegistryPostProcessor就应该要先来执行。接下来,我们就写一个实践案例来验证一番。

案例实践

首先,编写一个类,例如MyBeanDefinitionRegistryPostProcessor,它应要实现BeanDefinitionRegistryPostProcessor这个接口。

package com.meimeixia.ext;import org.springframework.beans.BeansException;import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;import org.springframework.beans.factory.support.AbstractBeanDefinition;import org.springframework.beans.factory.support.BeanDefinitionBuilder;import org.springframework.beans.factory.support.BeanDefinitionRegistry;import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;import org.springframework.beans.factory.support.RootBeanDefinition;import org.springframework.stereotype.Component;import com.meimeixia.bean.Blue;// 记住,我们这个组件写完之后,一定别忘了给它加在容器中@Componentpublic class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// TODO Auto-generated method stub System.out.println("MyBeanDefinitionRegistryPostProcessor...bean的数量:" + beanFactory.getBeanDefinitionCount()); } /** * 这个BeanDefinitionRegistry就是Bean定义信息的保存中心,这个注册中心里面存储了所有的bean定义信息, * 以后,BeanFactory就是按照BeanDefinitionRegistry里面保存的每一个bean定义信息来创建bean实例的。 * * bean定义信息包括有哪些呢?有这些,这个bean是单例的还是多例的、bean的类型是什么以及bean的id是什么。 * 也就是说,这些信息都是存在BeanDefinitionRegistry里面的。 */ @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
// TODO Auto-generated method stub System.out.println("postProcessBeanDefinitionRegistry...bean的数量:" + registry.getBeanDefinitionCount()); // 除了查看bean的数量之外,我们还可以给容器里面注册一些bean,我们以前也简单地用过 /* * 第一个参数:我们将要给容器中注册的bean的名字 * 第二个参数:BeanDefinition对象 */ // RootBeanDefinition beanDefinition = new RootBeanDefinition(Blue.class); // 现在我准备给容器中添加一个Blue对象 // 咱们也可以用另外一种办法,即使用BeanDefinitionBuilder这个构建器生成一个BeanDefinition对象,很显然,这两种方法的效果都是一样的 AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Blue.class).getBeanDefinition(); registry.registerBeanDefinition("hello", beanDefinition); }}

咱们编写的类实现BeanDefinitionRegistryPostProcessor接口之后,还得来实现两个方法,第一个方法,即postProcessBeanFactory,它来源于BeanFactoryPostProcessor接口里面定义的方法;第二个方法,即postProcessBeanDefinitionRegistry,它来源于BeanDefinitionRegistryPostProcessor接口里面定义的方法。

接下来,我们就来测试一下以上类里面的两个方法是什么时候执行的。运行IOCTest_Ext测试类中的test01方法,可以看到Eclipse控制台打印了如下内容。

在这里插入图片描述

可以看到,是我们自己写的MyBeanDefinitionRegistryPostProcessor类里面的postProcessBeanDefinitionRegistry方法先执行,该方法具体都做了哪些事呢?它先是拿到IOC容器中bean的数量(即10),再是向IOC容器中注册一个组件。接着,是我们自己写的MyBeanDefinitionRegistryPostProcessor类里面的postProcessBeanFactory方法再执行,该方法只是打印了一下IOC容器中bean的数量。你不仅要问了,为什么打印出的IOC容器中bean的数量是11,而不是10呢?这是因为我们之前已经向IOC容器中注册了一个组件。

除此之外,从Eclipse控制台输出的结果中我们还能看到,我们自己写的MyBeanDefinitionRegistryPostProcessor类里面的方法都执行完了以后,才轮到外面那些BeanFactoryPostProcessor来执行,执行的时候,不仅输出了IOC容器中bean的数量,而且还输出了每一个bean定义的名字。

现在我们是不是可以得出这样一个结论,BeanDefinitionRegistryPostProcessor是优先于BeanFactoryPostProcessor执行的,而且我们可以利用它给容器中再额外添加一些组件

源码分析

为什么BeanDefinitionRegistryPostProcessor是优先于BeanFactoryPostProcessor执行的呢?我们可以从源码的角度来深入分析一下。

首先,在我们自己写的MyBeanDefinitionRegistryPostProcessor类里面的两个方法上都打上一个断点,如下图所示。

在这里插入图片描述

然后,以debug的方式运行IOCTest_Ext测试类中的test01方法,如下图所示,程序现在停到了MyBeanDefinitionRegistryPostProcessor类里面的postProcessBeanDefinitionRegistry方法处。

在这里插入图片描述

那么程序是怎么运行到这儿的呢?我们不妨从IOCTest_Ext测试类中的test01方法开始,来梳理一遍整个流程。

鼠标单击Eclipse左上角方法调用栈中的IOCTest_Ext.test01() line:12,这时程序来到了IOCTest_Ext测试类的test01方法中,如下图所示。

在这里插入图片描述

可以看到现在是要来创建IOC容器的。

继续跟进代码,可以看到创建IOC容器时,最后还得刷新容器,如下图所示。

在这里插入图片描述

继续跟进代码,看这个refresh方法里面具体都做了些啥,如下图所示,可以看到它里面调用了如下一个invokeBeanFactoryPostProcessors方法。

在这里插入图片描述

其实这跟我们上一讲中分析BeanFactoryPostProcessor的原理是一模一样的,它也是在IOC容器创建对象的时候,会来调用invokeBeanFactoryPostProcessors这个方法。既然都是调用这个方法,那怎么能说BeanDefinitionRegistryPostProcessor就要优先于BeanFactoryPostProcessor执行呢?

我们继续跟进代码,发现又调用了一个invokeBeanFactoryPostProcessors方法,如下图所示。

在这里插入图片描述

继续跟进代码,可以看到又调用了如下一个invokeBeanDefinitionRegistryPostProcessors方法。

在这里插入图片描述

大家一定要注意这个方法哟😘!看清楚了,这个方法的名字叫invokeBeanDefinitionRegistryPostProcessors。此外,你还能看到传递进该方法的第一个参数是currentRegistryProcessors,那它又是在哪儿定义的呢?这就不得不好好看看PostProcessorRegistrationDelegate类中的invokeBeanFactoryPostProcessors方法了。

我们仔细查看该方法,会发现刚进入该方法时,就说明了不管什么时候都会优先调用BeanDefinitionRegistryPostProcessor。由于我们自己写的MyBeanDefinitionRegistryPostProcessor类实现了这个接口,所以它肯定会被先调用。

在这里插入图片描述

继续向下看,可以看到会取出所有实现了BeanDefinitionRegistryPostProcessor接口的类,即从容器中获取到所有的BeanDefinitionRegistryPostProcessor组件。然后,优先调用实现了PriorityOrdered接口的BeanDefinitionRegistryPostProcessor组件。

在这里插入图片描述

现在你该知道参数currentRegistryProcessors是在哪儿定义了吧,它其实就是一个保存BeanDefinitionRegistryPostProcessor组件的List集合。

调用完实现了PriorityOrdered接口的BeanDefinitionRegistryPostProcessor组件之后,接着会再调用实现了Ordered接口的BeanDefinitionRegistryPostProcessor组件。

在这里插入图片描述

最后再来调用剩余其他的BeanDefinitionRegistryPostProcessor组件,例如我们自己编写的MyBeanDefinitionRegistryPostProcessor类。

在这里插入图片描述

OK,现在程序停留在了上图所示的地方,很显然,此时已经从容器中获取到了所有的BeanDefinitionRegistryPostProcessor组件,说是所有,但实际上现在就只获取到了一个,即我们自己编写的MyBeanDefinitionRegistryPostProcessor类,如下图所示。

在这里插入图片描述

继续往下跟进代码,可以看到现在所做的事情就是从容器中获取到所有的BeanDefinitionRegistryPostProcessor组件之后,再来依次调用它们的postProcessBeanDefinitionRegistry方法。

在这里插入图片描述

所以,BeanDefinitionRegistryPostProcessor组件里面的postProcessBeanDefinitionRegistry方法会最优先被调用。

大家一定要注意哟😊,这儿是先来调用BeanDefinitionRegistryPostProcessor组件里面的postProcessBeanDefinitionRegistry方法的哟!想必你已经猜到了,接下来就应该调用BeanDefinitionRegistryPostProcessor组件里面的postProcessBeanFactory方法了。那这是从何处知道的呢?

回到PostProcessorRegistrationDelegate类的invokeBeanFactoryPostProcessors方法中,还是回到程序停留在如下图所示的地方,你会发现它下面紧挨着的地方有一个叫invokeBeanFactoryPostProcessors的方法。

在这里插入图片描述

点进去这个方法里面一看究竟,你就会恍然大悟了,原来是先调用完BeanDefinitionRegistryPostProcessor组件里面的postProcessBeanDefinitionRegistry方法,然后再来调用它里面的postProcessBeanFactory方法。你也应该要知道,该方法实际上是BeanFactoryPostProcessor接口里面定义的方法。

在这里插入图片描述

说了这么多,也只是针对BeanDefinitionRegistryPostProcessor组件来说的,我们知道了,它里面的方法,postProcessBeanDefinitionRegistry方法会先被调用,postProcessBeanFactory方法会后被调用。

这时,你不禁要问了,那些BeanFactoryPostProcessor组件又该是何时调用的呢?也就是说,这些BeanFactoryPostProcessor组件里面的postProcessBeanFactory方法又是什么时候被调用的呢?其实,我在上面已经说过了,BeanDefinitionRegistryPostProcessor组件是要优先于BeanFactoryPostProcessor组件执行的,也就是说,BeanFactoryPostProcessor组件(例如在上一讲中,我们自己写的MyBeanFactoryPostProcessor类)是之后才会被执行的,为什么我会这样说呢?

我们再来仔细看一下PostProcessorRegistrationDelegate类中的invokeBeanFactoryPostProcessors方法,只不过这时是从程序停留的地方(即第122行代码处)往下看,如下图所示。

在这里插入图片描述

看到这些代码,你是不是觉得很熟悉,这不就是咱们上一讲中已经看过的内容吗?不用我再讲一遍了吧😂,其实这不就是上一讲中我们讲解的BeanFactoryPostProcessor的整个执行顺序以及原理吗!

由此可见,这时会再来从容器中找到所有的BeanFactoryPostProcessor组件,然后依次触发其postProcessBeanFactory方法。

小结

分析了这么多,我们是不是可以来小结一下BeanDefinitionRegistryPostProcessor组件执行时的原理呢?

  1. 创建IOC容器
  2. 创建IOC容器时,要调用一个刷新方法,即refresh方法
  3. 从IOC容器中获取到所有的BeanDefinitionRegistryPostProcessor组件,并依次触发它们的postProcessBeanDefinitionRegistry方法,然后再来触发它们的postProcessBeanFactory方法
  4. 再来从IOC容器中获取到所有的BeanFactoryPostProcessor组件,并依次触发它们的postProcessBeanFactory方法

转载地址:https://liayun.blog.csdn.net/article/details/113781809 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:Spring注解驱动开发第38讲——你知道ApplicationListener的用法吗?
下一篇:Spring注解驱动开发第36讲——或许,这是你以前没看过的从源码角度理解BeanFactoryPostProcessor的原理

发表评论

最新留言

留言是一种美德,欢迎回访!
[***.207.175.100]2024年04月13日 20时01分45秒