【springboot源码分析】- 监听器的使用和源码分析

本博客源码地址
https://github.com/suchahaerkang/spring-boot-study.git

之前我们在【spring注解驱动开发】中学习过ApplicationListener&@EventListener,知道了监听器是用来监听发布的事件的。 现在我们来研究一下springboot中的监听器模式和我们如何自定义监听器以及注册方式和源码分析

1 springboot中的监听器模式

1.1 ApplicationListener

在这里插入图片描述

1.2 ApplicationEventMulticaster

在这里插入图片描述

1.3 ApplicationEvent

在这里插入图片描述

1.4 springboot中的事件发送顺序

在这里插入图片描述

1.5 springboot内部的监听器还是如何注册到容器中去的?

在这里插入图片描述
监听器注册到容器的方式和上一篇文章初始化器注册的方式一样,我这里就不截源码图了。我就用文字描述一下这个过程,想看这个过程的源码可以看一下这篇文章。springboot中监听器注册到容器中的过程:首先是从缓存中找有没有监听器,如果没有通过SpringFactoriesLoader这组件去根目录下的/META-INF/spring.factories文件里读取数据将其转换为Properties中,然后遍历获取value值,将value值通过逗号拆分成一个个类路径名保存在集合中并且保存在缓存中,然后通过这些集合匹配ApplicationListener类型,创建相应的实例存到集合中,最后给这个监听器实例的集合排序并复制给容器的listeners属性完成注册。
在这里插入图片描述

1.6 springboot内部监听事件触发机制?

从 springApplication.run(args)方法进去源码
在这里插入图片描述
在这里插入图片描述
我们进源码看一下getRunListeners(args)是怎么获取监听器的
在这里插入图片描述
因为1.5步将/META-INF/spring.factories里面的监听器实现类都获取封装之后放在了缓存中,所以我们可以直接从缓存中取出然后实例化他们
并在SpringApplicationRunListeners创建的时候赋给它的listeners属性

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

2 自定义监听器

注册自定义监听器一共有四种方式

2.1 通过SpringFactoriesLoader的方式

在这里插入图片描述
自定义一个监听器FirstApplicationListener监听ApplicationStartedEvent事件,Order为1

/**
 * @description:
 * @author: sukang
 * @date: 2020-03-25 9:18
 */
@Order(1)
public class FirstApplicationListener implements ApplicationListener<ApplicationStartedEvent> {

    @Override
    public void onApplicationEvent(ApplicationStartedEvent event) {
        System.out.println("FirstApplicationListener执行...");
    }
}

在/META-INF/spring.factories文件中将ApplicationListener的实现类配置进去

org.springframework.context.ApplicationListener=com.wolfx.springbootstudy.listener.FirstApplicationListener

启动容器

@SpringBootApplication
@MapperScan("com.wolfx.springbootstudy.dao")
public class SpringBootStudyApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootStudyApplication.class, args);

      /*  SpringApplication springApplication = new SpringApplication(SpringBootStudyApplication.class);
        //手动注册初始化器
        springApplication.addInitializers(new SecondInitializer());
        springApplication.run(args);*/
    }

}

在这里插入图片描述
这种就是通过SpringFactoriesLoader读spring.factories的方式注册的

  1. 硬编码手动注册的方式
    在这里插入图片描述
    重新写一个监听器
/**
 * @description:
 * @author: sukang
 * @date: 2020-03-25 9:18
 */
@Order(2)
public class SecondApplicationListener implements ApplicationListener<ApplicationStartedEvent> {

    @Override
    public void onApplicationEvent(ApplicationStartedEvent event) {
        System.out.println("SecondApplicationListener执行...");
    }
}

在启动类里手动注册监听器

@SpringBootApplication
@MapperScan("com.wolfx.springbootstudy.dao")
public class SpringBootStudyApplication {

    public static void main(String[] args) {
        //SpringApplication.run(SpringBootStudyApplication.class, args);

        SpringApplication springApplication = new SpringApplication(SpringBootStudyApplication.class);
        //手动注册监听器
        springApplication.addListeners(new SecondApplicationListener());
        springApplication.run(args);
    }

}

启动类启动
在这里插入图片描述
3)在application.properties的配置文件中配置key为context.listener.classes的value值
在这里插入图片描述
首先重新写个监听器的实现类

/**
 * @description:
 * @author: sukang
 * @date: 2020-03-25 9:18
 */
@Order(3)
public class ThirdApplicationListener implements ApplicationListener<ApplicationStartedEvent> {

    @Override
    public void onApplicationEvent(ApplicationStartedEvent event) {
        System.out.println("ThirdApplicationListener执行...");
    }
}

然后在配置文件中配置

#listener
context.listener.classes=com.wolfx.springbootstudy.listener.ThirdApplicationListener

启动类

@SpringBootApplication
@MapperScan("com.wolfx.springbootstudy.dao")
public class SpringBootStudyApplication {

    public static void main(String[] args) {
        //SpringApplication.run(SpringBootStudyApplication.class, args);

        SpringApplication springApplication = new SpringApplication(SpringBootStudyApplication.class);
        //手动注册监听器
        springApplication.addListeners(new SecondApplicationListener());
        springApplication.run(args);
    }

}

运行结果
在这里插入图片描述
从运行结果看我们发现ThirdApplicationListener 的Order值最大,但是第一个执行,不合逻辑。因为一般Order值小的先执行,下面我们分析一下为什么会出现这种情况,看源码我们知道产生这种结果的由于springboot中的DelegatingApplicationListener这个类起的作用,这个类
也是一个实现ApplicationListener的监听器,当它监听到事件之后执行它的onApplicationEvent()方法,这个方法就是去配置文件中获取key为
"context.listener.classes"的value值然后包装遍历执行他们的onApplicationEvent()。又由于DelegatingApplicationListener的order值为0。所以ThirdApplicationListener最先执行。
以上三种方式跟上篇博客中的注册初始化器的方式一模一样。这三种方式都只支持监听一个事件。

4) 下面第四种方式是可以支持监听多个事件的,但是自定义的监听器要实现SmartApplicationListener的supportsEventType()方法。
在这里插入图片描述
代码如下

/**
 * @description:
 * @author: sukang
 * @date: 2020-03-25 9:18
 */
@Order(4)
public class FourApplicationListener implements SmartApplicationListener {

    @Override
    public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
        //自定义多个监听的事件
        return ApplicationStartedEvent.class.isAssignableFrom(eventType) || ApplicationPreparedEvent.class.isAssignableFrom(eventType);
    }

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        System.out.println("FourApplicationListener执行...");
    }
}

在spring.factories文件中进行配置

org.springframework.context.ApplicationListener=com.wolfx.springbootstudy.listener.FirstApplicationListener,com.wolfx.springbootstudy.listener.FourApplicationListener

运行结果
在这里插入图片描述
在这里插入图片描述

3 面试题

1)介绍一下监听器模式?
监听器模式中有三个角色,分别是监听器,事件和事件多播器。监听器会指定监听的的事件。事件多播器是用来管理监听器,并且给管理的这些监听器分发事件。

2)springboot中有哪些框架事件以及他们的顺序?
在这里插入图片描述
3)ApplicationListener和SmartApplicationListener的区别?
ApplicationListener只能监听一个事件,而SmartApplicationListener可以监听多个事件。

发布了88 篇原创文章 · 获赞 34 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/suchahaerkang/article/details/105087139