36-49扩展原理

脑图
TODOSpring注解驱动开发第36讲——或许,这是你以前没看过的从源码角度理解BeanFactoryPostProcessor的原理
TODO Spring注解驱动开发第37讲——你知SpringBeanDefinitionRegistryPostProcessor是如何执行的吗?
TODO Spring注解驱动开发第38讲——你知道ApplicationListener的用法吗?
TODO Spring注解驱动开发第39讲——你不知道的ApplicationListener的原理
Spring注解驱动开发第40讲——你晓得@EventListener这个注解的原理吗?
Spring注解驱动开发第41-47讲——Spring IOC容器创建源码解析(一)之BeanFactory的创建以及预准备工作(合起来整个过程)

Spring注解驱动开发第36讲——或许,这是你以前没看过的从源码角度理解BeanFactoryPostProcessor的原理

在前面,我们学习了一下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;

@Component
public 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")
@Configuration
public 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")
@Configuration
public 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从名为nonOrderedPostProcessors的List集合中拿就可以了。

下面我们来仔细分析一下PostProcessorRegistrationDelegate类中的invokeBeanFactoryPostProcessors方法具体都做了哪些操作。

首先,来看一下如下图所示的这行代码,这行代码说的是拿到所有BeanFactoryPostProcessor组件的名字。

在这里插入图片描述
然后,来挨个看相应名字的BeanFactoryPostProcessor组件,哪些是实现了PriorityOrdered接口的,哪些是实现了Ordered接口的,以及哪些是什么接口都没有实现的,说的简单一点就是将不同的BeanFactoryPostProcessor组件给分离出来。
在这里插入图片描述

接着,分别按不同的执行顺序来处理三种不同的BeanFactoryPostProcessor组件。

在这里插入图片描述

由于咱们自己编写的BeanFactoryPostProcessor既没有实现PriorityOrdered接口,也没有实现Ordered接口,所以就按照最后一种顺序来执行。

在这里插入图片描述

以上就是对PostProcessorRegistrationDelegate类中的invokeBeanFactoryPostProcessors方法大致分析。分析完之后,我们继续跟进代码,会发现其遍历了所有的BeanFactoryPostProcessor组件,我们自己编写的实现了BeanFactoryPostProcessor接口的MyBeanFactoryPostProcessor类肯定也属于其中,所以会被遍历到,然后便会执行其postProcessBeanFactory方法。

在这里插入图片描述
以上就是我们从源码的角度分析了一下BeanFactoryPostProcessor的整个执行顺序以及原理。

小结

经过以上源码分析,我们可以得出这样一个简单结论:首先从IOC容器中找到所有类型是BeanFactoryPostProcessor的组件,然后再来执行它们其中的方法,而且是在初始化创建其他组件前面执行。

我为什么可以这么肯定地说呢?如果我们大家还有回忆的话,那么你一定记得在我讲解AOP原理的时候,bean的创建与初始化还在很后面,如下图所示。
在这里插入图片描述

Spring注解驱动开发第37讲——你知SpringBeanDefinitionRegistryPostProcessor是如何执行的吗?

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

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

在这里插入图片描述

从源码角度理解BeanDefinitionRegistryPostProcessor的原理 初步认识一下BeanDefinitionRegistryPostProcessor

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

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

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

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

案例实践

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

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

// 记住,我们这个组件写完之后,一定别忘了给它加在容器中
@Component
public 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);
	}

}

接下来,我们就来测试一下以上类里面的两个方法是什么时候执行的。运行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组件执行时的原理呢?

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

Spring注解驱动开发第38讲——你知道ApplicationListener的用法吗?

在前面两讲中,我们学习了一下Spring扩展原理里面的BeanFactoryPostProcessor和BeanDefinitionRegistryPostProcessor。在这一讲中,我们将会学习一下Spring剩余其他扩展原理里面的ApplicationListener。

你不知道的ApplicationListener

ApplicationListener的概述

ApplicationListener按照字面意思,它应该是Spring里面的应用监听器,也就是Spring为我们提供的基于事件驱动开发的功能。

接下来,我们看一下ApplicationListener的源码,如下图所示,可以看到它是一个接口。

在这里插入图片描述

也就是说,如果我们要写一个监听器,那么我们要写的监听器就得实现这个接口,而该接口中带的泛型就是我们要监听的事件。也就是说,我们应该要监听ApplicationEvent及其下面的子事件,因此,如果我们要发布事件,那么所发布的事件应该是ApplicationEvent的子类。

ApplicationListener的作用

它的作用主要是来监听IOC容器中发布的一些事件,只要事件发生便会来触发该监听器的回调,从而来完成事件驱动模型的开发。

ApplicationListener的用法

首先,编写一个类来实现ApplicationListener接口,例如MyApplicationListener,这实际上就是写了一个监听器。

package com.meimeixia.ext;

import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

// 当然了,监听器这东西要工作,我们还得把它添加在容器中
@Component
public class MyApplicationListener implements ApplicationListener<ApplicationEvent> {

	// 当容器中发布此事件以后,下面这个方法就会被触发
	@Override
	public void onApplicationEvent(ApplicationEvent event) {
		// TODO Auto-generated method stub
		System.out.println("收到事件:" + event);
	}

}

然后,我们就要来测试一下以上监听器的功能了。试着运行IOCTest_Ext测试类中的test01方法,看能不能收到事件?

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();
	}

}

如果运行以上test01方法,那么你将会看到Eclipse控制台打印出了如下内容。
在这里插入图片描述
哎,可以看到我们收到了两个事件,这两个事件分别是org.springframework.context.event.ContextRefreshedEvent和org.springframework.context.event.ContextClosedEvent,其中第一个是容器已经刷新完成事件,第二个是容器关闭事件。而且,从下图中可以看到,这两个事件都是ApplicationEvent下面的事件。
在这里插入图片描述
只不过现在暂时还没用到容器开始和容器停止这两个事件而已。其实,想必你也已经猜到了,IOC容器在刷新完成之后便会发布ContextRefreshedEvent事件,一旦容器关闭了便会发布ContextClosedEvent事件。

这时,你不禁要问了,我们可不可以自己发布事件呢?当然可以了,只不过此时我们应该遵循如下的步骤来进行开发。

第一步,写一个监听器来监听某个事件。当然了,监听的这个事件必须是ApplicationEvent及其子类。

第二步,把监听器加入到容器中,这样Spring才能知道有这样一个监听器。

第三步,只要容器中有相关事件发布,那么我们就能监听到这个事件。举个例子,就拿我们上面监听的两个事件来说,你要搞清楚的一个问题是谁发布了这两个事件,猜都能猜得到,这两个事件都是由Spring发布的。

ContextRefreshedEvent:容器刷新完成事件。即容器刷新完成(此时,所有bean都已完全创建),便会发布该事件。
ContextClosedEvent:容器关闭事件。即容器关闭时,便会发布该事件。

其实,在上面我们也看到了,Spring还默认定义了一些其他事件。除此之外,我们自己也可以编写一些自定义事件。但是,问题的关键是我们能不能自己发布事件呢?答案是可以。

第四步,我们自己来发布一个事件。而发布一个事件,我们需要像下面这么来做。

在这里插入图片描述此时,运行以上test01方法,你将会看到Eclipse控制台打印出了如下内容。
在这里插入图片描述

除了能收到容器刷新完成和容器关闭这俩事件之外,还能收到我们调用applicationContext发布出去的事件。只要把这个事件发布出去,那么我们自己编写的监听器就能监听到这个事件。

以上就是ApplicationListener应用监听器的使用。那么,ApplicationListener到底是怎么工作的呢?我们下一讲就来讲讲它内部的原理。

Spring注解驱动开发第39讲——你不知道的ApplicationListener的原理

我们不妨先回顾一下上一讲所学习的内容,在上一讲中,我们是编写了一个监听器,即ApplicationListener接口的一个实现类,通过这个监听器,我们可以来监听容器中的事件,只要容器中有事件发布,监听器中的方法就会得到回调。除此之外,我们还可以自己利用IOC容器的publishEvent方法来自定义发布一个事件,也就是说,我们自己也是能够发布一个事件的。

回顾完毕,接下来,咱们就来说说本讲所要阐述的内容,即事件的整个发布和事件监听机制的内部原理。

事件监听机制的源码分析

在研究分析事件的整个发布和事件监听机制的内部原理之前,我们先来运行一下如下单元测试类中的test01方法。

运行完毕,你会发现Eclipse控制台打印出了如下三个咱们收到的事件。

ContextRefreshedEvent事件
我们自己发布的一个事件,即IOCTest_Ext$1[source=我发布的事件]
ContextClosedEvent事件

在这里插入图片描述
接下来,我们就要来分析一下以上这三个事件都是怎么收到的?

创建容器并且刷新

首先,我们在自己编写的监听器(例如MyApplicationListener)内的onApplicationEvent方法处打上一个断点,如下图所示。

在这里插入图片描述然后,以debug的方式运行IOCTest_Ext测试类中的test01方法,如下图所示,程序现在停到了咱们自己编写的监听器的onApplicationEvent方法中。

在这里插入图片描述很明显,现在我们看到的是收到的第一个事件,即ContextRefreshedEvent事件。那么问题来了,我们是怎么收到的该事件呢?我们不妨从IOCTest_Ext测试类中的test01方法开始,来梳理一遍整个流程。

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

在这里插入图片描述

可以看到第一步是要来创建IOC容器的。继续跟进代码,可以看到在创建容器的过程中,还会调用一个refresh方法来刷新容器,刷新容器其实就是创建容器里面的所有bean。

在这里插入图片描述
继续跟进代码,看这个refresh方法里面具体都做了些啥,如下图所示,可以看到它里面调用了如下一个finishRefresh方法,顾名思义,该方法就是来完成容器的刷新工作的。

在这里插入图片描述对于这个refresh方法而言,想必你是再熟悉不过了,它里面做了很多的事情,也就是说,在容器刷新这一步中做了很多的事情,比如执行BeanFactoryPostProcessor组件的方法、给容器中注册后置处理器等等,这些之前我就已经详细讲解过了,故在这儿只是稍微提及一下,并不再过多赘述。

容器刷新完成,发布ContextRefreshedEvent事件

当容器刷新完成时,就会调用finishRefresh方法,那么该方法里面又做了哪些事呢?我们继续跟进代码,如下图所示,发现容器刷新完成时调用的finishRefresh方法里面又调用了一个叫publishEvent的方法,而且传递进该方法的参数是new出来的一个ContextRefreshedEvent对象。这一切都在说明着,容器在刷新完成以后,便会发布一个ContextRefreshedEvent事件。

在这里插入图片描述
接下来,我们就来看看ContextRefreshedEvent事件的发布流程。

事件发布流程

当容器刷新完成时,就会来调用一个叫publishEvent的方法,而且会向该方法中传递一个ContextRefreshedEvent对象。这即是发布了一个事件,这个事件呢,正是我们第一个感知到的事件,即容器刷新完成事件。接下来,我们就来看看这个事件到底是怎么发布的。

继续跟进代码,可以看到程序来到了如下图所示的地方。

在这里插入图片描述
我们继续跟进代码,可以看到程序来到了如下图所示的这行代码处。

在这里插入图片描述可以看到先是调用一个getApplicationEventMulticaster方法,从该方法的名字中就可以看出,它是来获取事件多播器的,不过也有人叫事件派发器。接下来,我们就可以说说ContextRefreshedEvent事件的发布流程了。

首先,调用getApplicationEventMulticaster方法来获取到事件多播器,或者,你叫事件派发器也行。所谓的事件多播器就是指我们要把一个事件发送给多个监听器,让它们同时感知。

然后,调用事件多播器的multicastEvent方法,这个方法就是用来向各个监听器派发事件的。那么,它到底是怎么来派发事件的呢?

继续跟进代码,来好好看看multicastEvent方法是怎么写的,如下图所示。
在这里插入图片描述

可以看到,一开始就有一个for循环,在这个for循环中,有一个getApplicationListeners方法,它是来拿到所有的ApplicationListener的,拿到之后就会来挨个遍历再来拿到每一个ApplicationListener。

很快,你会看到有一个if判断,它会判断getTaskExecutor方法能不能够返回一个Executor对象,如果能够,那么会利用Executor的异步执行功能来使用多线程的方式异步地派发事件;如果不能够,那么就使用同步的方式直接执行ApplicationListener的方法。

细心一点的同学,可以点进去Executor里面去看一看,你会发现它是一个接口,并且Spring提供了一个叫TaskExecutor的子接口来继承它。在该子接口下,Spring又提供了一个SyncTaskExecutor类来实现它,以及一个AsyncTaskExecutor接口来继承它,如下图所示。

在这里插入图片描述

在这里插入图片描述

不用我说,大家都应该知道,SyncTaskExecutor支持以同步的方式来执行某一任务,AsyncTaskExecutor支持以异步的方式来执行某一任务。也就是说,我们可以在自定义事件派发器的时候(这个后面就会讲到),给它传递这两种类型的TaskExecutor,让它支持以同步或者异步的方式来派发事件。

现在程序很显然是进入到了else判断语句中,也就是说,现在是使用同步的方式来直接执行ApplicationListener的方法的,相应地,这时是调用了一个叫invokeListener的方法,而且在该方法中传入了当前遍历出来的ApplicationListener。那么问题来了,这个方法的内部又做了哪些事呢?

我们继续跟进代码,可以看到程序来到了如下图所示的地方。这时,invokeListener方法里面调用了一个叫doInvokeListener的方法。

在这里插入图片描述

继续跟进代码,可以看到程序来到了如下图所示的这行代码处。看到这儿,你差不多应该知道了这样一个结论,即遍历拿到每一个ApplicationListener之后,会回调它的onApplicationEvent方法。

在这里插入图片描述

继续跟进代码,这时,程序就会来到我们自己编写的监听器(例如MyApplicationListener)中,继而来回调它其中的onApplicationEvent方法。

在这里插入图片描述

以上就是ContextRefreshedEvent事件的发布流程。

写到这里,我来做一下总结,即总结一下一个事件怎么发布的。首先调用一个publishEvent方法,然后获取到事件多播器,接着为我们派发事件。你看,就是这么简单!

在本讲的最开始,从Eclipse控制台打印出的内容中,我们可以知道收到的第一个事件就是ContextRefreshedEvent事件。为了让大家能够更加清晰地看到这一点,我按下F6快捷键让程序继续往下运行,如下图所示,这时Eclipse控制台打印出了收到的第一个事件,即ContextRefreshedEvent事件。

在这里插入图片描述

自己发布的事件

按下F8快捷键让程序运行到下一个断点,如下图所示,这时是来到了我们自己编写的监听器(例如MyApplicationListener)里面的onApplicationEvent方法中。

在这里插入图片描述

这里,我们要明白一点,这儿是我们自己发布的事件,就是调用容器的publishEvent方法发布出去的事件,这可以从test01方法的如下这行代码处看出。

在这里插入图片描述
接下来,我们就要来看一下咱们自己发布的事件的发布流程了。

这里,我要说一嘴,其实,咱们自己发布的事件的发布流程与上面所讲述的ContextRefreshedEvent事件的发布流程是一模一样的,我为什么会这么说呢,这得看接下来的源码分析了。

继续跟进代码,可以看到程序来到了如下图所示的地方,这不是还是再调用publishEvent方法吗?

在这里插入图片描述

我们继续跟进代码,可以看到程序来到了如下图所示的这行代码处。

在这里插入图片描述

在这里插入图片描述

可以看到,还是先获取到事件多播器,然后再调用事件多播器的multicastEvent方法向各个监听器派发事件。

继续跟进代码,可以看到multicastEvent方法是像下面这样写的。

在这里插入图片描述
依然还是拿到所有的ApplicationListener,然后再遍历拿到每一个ApplicationListener,接着来挨个执行每一个ApplicationListener的方法。怎么来执行呢?如果是异步模式,那么就使用异步的方式来执行,否则便使用同步的方式直接执行。

继续跟进代码,可以看到程序来到了如下图所示的地方。这时,invokeListener方法里面调用了一个叫doInvokeListener的方法。

在这里插入图片描述
继续跟进代码,可以看到程序来到了如下图所示的这行代码处。依旧能看到,这是遍历拿到每一个ApplicationListener之后,再来回调它的onApplicationEvent方法。

在这里插入图片描述

以上就是咱们自己发布的事件的发布流程。

最后,我做一下小结,不管是容器发布的事件,还是咱们自己发布的事件,都会走以上这个事件发布流程,即先拿到事件多播器,然后再拿到所有的监听器,接着再挨个回调它的方法。

容器关闭,发布ContextClosedEvent事件

接下来,可想而知,就应该是要轮到最后一个事件了,即容器关闭事件。我们按下F8快捷键让程序运行到下一个断点,如下图所示,可以看到Eclipse控制台打印出了收到的第二个事件,即我们自己发布的事件。

在这里插入图片描述
而且,从上图中也能看到,这时程序来到了我们自己编写的监听器(例如MyApplicationListener)里面的onApplicationEvent方法中。

下面,我们就来看看容器关闭事件的发布流程。首先,鼠标单击Eclipse左上角方法调用栈中的IOCTest_Ext.test01() line:20,这时程序来到了IOCTest_Ext测试类的test01方法中的最后一行代码处,如下图所示。

在这里插入图片描述以上这行代码说的就是来关闭容器,那么容器是怎么关闭的呢?我们继续跟进代码,发现关闭容器的close方法里面又调用了一个doClose方法,如下图所示。

在这里插入图片描述

继续跟进代码,如下图所示,可以看到doClose方法里面又调用了一个publishEvent方法,而且传递进该方法的参数是new出来的一个ContextClosedEvent对象。这一切都在说明着,关闭容器,会发布一个ContextClosedEvent事件。
在这里插入图片描述
当然不管怎么发布,ContextClosedEvent事件所遵循的发布流程和上面讲述的一模一样。

有关事件多播器,这些是你应该知道的

你知道事件多播器是怎么拿到的吗?

接下来,我们得来说另一点了。你是不是注意到了这一点,在上面我们讲述事件发布的流程时,会通过一个getApplicationEventMulticaster方法来获取事件多播器,我们不妨看一下该方法是怎么写的,如下图所示。

在这里插入图片描述

通过该方法可以获取到事件多播器,很显然,applicationEventMulticaster这么一个玩意代表的就是事件多播器。那么问题来了,我们是从哪获取到的事件多播器的呢?下面我就要为大家揭晓谜团了,给大家说一下它是怎么拿到的。

首先,创建IOC容器。我们知道,在创建容器的过程中,还会调用一个refresh方法来刷新容器,如下图所示。

在这里插入图片描述

然后,我们就要来看看这个refresh方法具体都做了哪些事。该方法我们已经很熟悉了,如下图所示,可以看到在该方法中会调非常多的方法,其中就有一个叫initApplicationEventMulticaster的方法,顾名思义,它就是来初始化ApplicationEventMulticaster的。而且,它还是在初始化创建其他组件之前调用的。

在这里插入图片描述

在这里插入图片描述那么,初始化ApplicationEventMulticaster的逻辑又是怎样的呢?我们也可以来看一看,进入initApplicationEventMulticaster方法里面,如下图所示。

在这里插入图片描述

上述这个方法,你能看得懂吗?其实很简单,它就是先判断IOC容器(也就是BeanFactory)中是否有id等于applicationEventMulticaster的组件,这个我是咋知道的呢?因为APPLICATION_EVENT_MULTICASTER_BEAN_NAME这个常量就是字符串applicationEventMulticaster,如下图所示。

在这里插入图片描述
如果IOC容器中有id等于applicationEventMulticaster的组件,那么就会通过getBean方法直接拿到这个组件;如果没有,那么就重新new一个SimpleApplicationEventMulticaster类型的事件多播器,然后再把这个事件多播器注册到容器中,也就是说,这相当于我们自己给容器中注册了一个事件多播器,这样,以后我们就可以在其他组件要派发事件的时候,自动注入这个事件多播器就行了。其实说白了,在整个事件派发的过程中,我们可以自定义事件多播器。

以上就是我们这个事件多播器它是怎么拿到的。

你知道容器是怎么将容器中的监听器注册到事件多播器中去的吗?

还记得我们在分析事件发布流程时,有一个叫getApplicationListeners的方法吗?

在这里插入图片描述通过该方法就能知道容器中有哪些监听器。

那么问题来了,容器中到底有哪些监听器呢?其实,这个问题的答案很简单,因为我们把监听器早就已经添加到了容器中,所以,容器只需要判断一下哪些组件是监听器就行了。我为什么会这么说呢?这就得分析源码才能得出了。

首先,依旧还是创建IOC容器。我们也知道,在创建容器的过程中,还会调用一个refresh方法来刷新容器,如下图所示。

在这里插入图片描述

然后,我们就要来看看这个refresh方法具体都做了哪些事。该方法我们已经超熟悉了,如下图所示,可以看到在该方法中会调非常多的方法,其中就有一个叫registerListeners的方法,顾名思义,它就是来注册监听器的。

在这里插入图片描述
那到底是怎么来注册监听器的呢?我们可以点进去该方法里面看一看,如下图所示,可以看到它是先从容器中拿到所有的监听器,然后再把它们注册到applicationEventMulticaster当中。

在这里插入图片描述

当然了,第一次调用该方法时,getApplicationListeners方法是获取不到容器中所有的监听器的,因为这些监听器还没注册到容器中,不知道我这样理解的对不对,如果我要是理解的不对,那么还请读者指出,我再来修改,哈哈哈😁

所以,第一次调用该方法时,它会调用getBeanNamesForType方法从容器中拿到所有ApplicationListener类型的组件(即监听器),然后再把这些组件注册到事件派发器中。

在这里插入图片描述

这样,事件派发器里面就有这些监听器了,容器中到底有哪些监听器我们自然也就知道了。

接下来,就不用我说了吧!自然是事件派发器向各个监听器派发事件了。

Spring注解驱动开发第40讲——你晓得@EventListener这个注解的原理吗?

在上一讲中,我们讲了一下事件监听机制的内部原理,当然了,在一过程中,我们也看到了事件的整个发布流程。再回顾一下的话,你会发现之前咱们编写的监听器都是来实现ApplicationListener这个接口的,其实,除此之外,还有另外一种方式。因此,这一讲,我们就来着重讲述这种方式。

这里我先提一下这种方式,即使用@EventListener注解,我们就可以让任意方法都能监听事件。这样的话,我们在一个普通的业务逻辑组件中,就可以直接来使用这个注解了,而不是让它去实现ApplicationListener这个接口。

@EventListener注解的用法

首先,编写一个普通的业务逻辑组件,例如UserService,并在该组件上标注一个@Service注解。

package com.meimeixia.ext;

import org.springframework.stereotype.Service;

@Service
public class UserService {
	
}

在该组件内,我们肯定会写一些很多的方法,但这里就略去了。那么问题来了,如果我们希望该组件能监听到事件,那么该怎么办呢?我们可以在该组件内写一个listen方法,以便让该方法来监听事件。这时,我们只需要简单地给该方法上标注一个@EventListener注解,就可以让它来监听事件了。那么,到底要监听哪些事件呢?我们可以通过@EventListener注解中的classes属性来指定,例如,我们可以让listen方法监听ApplicationEvent及其下面的子事件。

package com.meimeixia.ext;

import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;

@Service
public class UserService {
	
	// 一些其他的方法...

	@EventListener(classes=ApplicationEvent.class)
	public void listen() {
		System.out.println("UserService...");
	}
	
}

当然了,我们还可以通过@EventListener注解中的classes属性来指定监听多个事件。

package com.meimeixia.ext;

import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;

@Service
public class UserService {
	
	// 一些其他的方法...

	// @EventListener(classes=ApplicationEvent.class)
	@EventListener(classes={ApplicationEvent.class})
        public void listen() {
            System.out.println("UserService...");
        }
	
}

如果ApplicationEvent及其下面的子事件发生了,那么我们应该怎么办呢?想都不用想,肯定是拿到这个事件,因此我们就要在listen方法的参数位置上写一个ApplicationEvent参数来接收该事件。

package com.meimeixia.ext;

import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;

@Service
public class UserService {
	
	// 一些其他的方法...

	// @EventListener(classes=ApplicationEvent.class)
	@EventListener(classes={ApplicationEvent.class})
	public void listen(ApplicationEvent event) {
		System.out.println("UserService...监听到的事件:" + event);
	}
	
}

以上就是我们自己编写的一个普通的业务逻辑组件,该组件就能监听事件,这跟实现ApplicationListener接口的效果是一模一样的。

然后,我们就要来进行测试了,就是运行一下以下IOCTest_Ext测试类中的test01方法。

package com.meimeixia.test;

import org.junit.Test;
import org.springframework.context.ApplicationEvent;
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.publishEvent(new ApplicationEvent(new String("我发布的事件")) {
		});
		
		// 关闭容器
		applicationContext.close();
	}

}

你会发现Eclipse控制台打印出了如下内容,可以清晰地看到,不仅我们之前编写的监听器(例如MyApplicationListener)收到了事件,而且UserService组件也收到了事件。也就是说,每一个都能正确地收到事件。

在这里插入图片描述

这里我得说一嘴,以后咱们对@EventListener这个注解的使用会比较多,因为它使用起来非常方便。

接下来,我们就得说说这个注解背后的原理了。

@EventListener注解的原理

我们可以点进去@EventListener这个注解里面去看一看,如下图所示,可以看到这个注解上面有一大堆的描述,从描述中我们是否可以猜到这个注解的内部工作原理呢?答案是可以的。
在这里插入图片描述

描述中有一个醒目的字眼,即参考EventListenerMethodProcessor。意思可能是说,如果你想搞清楚@EventListener注解的内部工作原理,那么可以参考EventListenerMethodProcessor这个类。

EventListenerMethodProcessor是啥呢?它就是一个处理器,其作用是来解析方法上的@EventListener注解的。这也就是说,**Spring会使用EventListenerMethodProcessor这个处理器来解析方法上的@EventListener注解。**因此,接下来,我们就要将关注点放在这个处理器上,搞清楚这个处理器是怎样工作的。搞清楚了这个,自然地我们就搞清楚了@EventListener注解的内部工作原理。

我们点进去EventListenerMethodProcessor这个类里面去看一看,如下图所示,发现它实现了一个接口,叫SmartInitializingSingleton。这时,要想搞清楚EventListenerMethodProcessor这个处理器是怎样工作的,那就得先搞清楚SmartInitializingSingleton这个接口的原理了。

在这里插入图片描述
不妨点进去SmartInitializingSingleton这个接口里面去看一看,你会发现它里面定义了一个叫afterSingletonsInstantiated的方法,如下图所示。

在这里插入图片描述

接下来,我们就要搞清楚到底是什么时候开始触发执行afterSingletonsInstantiated方法的。

仔细看一下SmartInitializingSingleton接口中afterSingletonsInstantiated方法上面的描述信息,不难看出该方法是在所有的单实例bean已经全部被创建完了以后才会被执行。

其实,在介绍SmartInitializingSingleton接口的时候,我们也能从描述信息中知道,在所有的单实例bean已经全部被创建完成以后才会触发该接口。紧接着下面一段的描述还说了,该接口的调用时机有点类似于ContextRefreshedEvent事件,即在容器刷新完成以后,便会回调该接口。也就是说,这个时候容器已经创建完了。

好吧,回到主题,我们来看看afterSingletonsInstantiated方法的触发时机。首先,我们得在EventListenerMethodProcessor类里面的afterSingletonsInstantiated方法处打上一个断点,如下图所示。
在这里插入图片描述
然后,以debug的方式运行IOCTest_Ext测试类中的test01方法,这时程序停留在了EventListenerMethodProcessor类里面的afterSingletonsInstantiated方法中,如下图所示。

在这里插入图片描述此时,你是不是很想知道是什么时候开始触发执行afterSingletonsInstantiated这个方法的呢?我们不妨从IOCTest_Ext测试类中的test01方法开始,来梳理一遍整个流程。

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

在这里插入图片描述可以看到第一步是要来创建IOC容器的。继续跟进代码,可以看到在创建容器的过程中,还会调用一个refresh方法来刷新容器,刷新容器其实就是创建容器里面的所有bean。

在这里插入图片描述
继续跟进代码,看这个refresh方法里面具体都做了些啥,如下图所示,可以看到它里面调用了如下一个finishBeanFactoryInitialization方法,顾名思义,该方法就是来完成BeanFactory的初始化工作的。

在这里插入图片描述
对于以上这个方法,我相信大家都不会陌生,因为我们之前就看过好多遍了,它其实就是来初始化所有剩下的那些单实例bean的。也就是说,如果还有一些单实例bean还没被初始化,即还没创建对象,那么便会在这一步进行(初始化)。

继续跟进代码,如下图所示,可以看到在finishBeanFactoryInitialization方法里面执行了如下一行代码,依旧还是来初始化所有剩下的单实例bean。

在这里插入图片描述

继续跟进代码,如下图所示,可以看到现在程序停留在了如下这行代码处。

在这里插入图片描述这不就是我们要讲的afterSingletonsInstantiated方法吗?它原来是在这儿调用的啊!接下来,咱们就得好好看看在调用该方法之前,具体都做了哪些事。

由于afterSingletonsInstantiated方法位于DefaultListableBeanFactory类的preInstantiateSingletons方法里面,所以我们就得来仔细看看preInstantiateSingletons方法里面具体都做了些啥了。

进入眼帘的首先是一个for循环,在该for循环里面,beanNames里面存储的都是即将要创建的所有bean的名字,紧接着会做一个判断,即判断bean是不是抽象的,是不是单实例的,等等等等。最后,不管怎样,都会调用getBean方法来创建对象。

在这里插入图片描述
在这里插入图片描述

总结一下就是,先利用一个for循环拿到所有我们要创建的单实例bean,然后挨个调用getBean方法来创建对象。也即,创建所有的单实例bean。

再来往下翻阅preInstantiateSingletons方法,发现它下面还有一个for循环,在该for循环里面,beanNames里面依旧存储的是即将要创建的所有bean的名字。那么,在该for循环中所做的事情又是什么呢?很显然,在最上面的那个for循环中,所有的单实例bean都已经全部创建完了。因此,在下面这个for循环中,咱们所要做的事就是获取所有创建好的单实例bean,然后判断每一个bean对象是否是SmartInitializingSingleton这个接口类型的,如果是,那么便调用它里面的afterSingletonsInstantiated方法,而该方法就是SmartInitializingSingleton接口中定义的方法。

在这里插入图片描述
在这里插入图片描述

至此,你该搞清楚afterSingletonsInstantiated方法是什么时候开始触发执行了吧😁!就是在所有单实例bean全部创建完成以后。

最后,我还得说一嘴。如果所有的单实例bean都已经创建完了,也就是说下面这一步都执行完了,那么说明IOC容器已经创建完成了。

在这里插入图片描述
在这里插入图片描述
那么,紧接着便会来调用finishRefresh方法,容器已经创建完了,此时就会来发布容器已经刷新完成的事件。这就呼应了开头的那句话,即SmartInitializingSingleton接口的调用时机有点类似于ContextRefreshedEvent事件,即在容器刷新完成以后,便会回调该接口。

以上就是@EventListener注解的内部工作原理,在讲解该原理时,我们顺道说了一下SmartInitializingSingleton接口的原理。

Spring注解驱动开发第41讲——Spring IOC容器创建源码解析(一)之BeanFactory的创建以及预准备工作

在前面,我们已经学会了怎样来使用ApplicationListener,也研究了一下其内部原理。而从这一讲开始,我们就要结合我们以前学过的所有内容,来梳理一下Spring整个容器的创建以及初始化过程。我是希望通过对Spring源码的整个分析,令大家对Spring内部的工作原理以及运行机制能有一个更深刻的理解。

接下来,我们来分析并详细记录一下Spring容器的创建以及初始化过程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

明月常新

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值