MENU

springboot源码解析(一)ApplicationListener

October 13, 2020 • Read: 2320 • 后端

0、前提

本文分析springboot中的listener只是简单的分析下,关键部分的地方都给了注释的,其余有些细节的地方并没有去分析,感兴趣的读者可以自行去分析下。(其实是懒!逃......)
如果还不了解观察者设计模式的,可以看看这篇文章:springboot 源码解析(一)ApplicationListener

1、springboot Listener

1.1 自定义listener

  1. 可以实现GenericApplicationListener接口,GenericApplicationListener继承自ApplicationListener,ApplicationListener接口里面只有一个onApplicationEvent()方法,GenericApplicationListener接口里面提供了其余的几个方法,GenericApplicationListener接口相当于对其父类进行了增强,使listener订阅事件的方式增加。

    // supportsEventType()和supportsSourceType()方法必须同时返回true才代表对这个事件感兴趣
    // 这个类有三个地方可以对事件进行订阅
    public class MyListener implements GenericApplicationListener {
    
        // (1)这里也可以对事件进行订阅,对自己感兴趣的事件执行相关逻辑
        public void onApplicationEvent(ApplicationEvent event) {
            System.out.println("-----------myListener");
        }
    
        // (2)表示你对什么时间感兴趣,直接返回true代表对所有的事件都感兴趣,
        // 可以通过参数 eventType来设置自己感兴趣的。简单来说就是订阅事件
        public boolean supportsEventType(ResolvableType eventType) {
            return true;
        }
        // (2)直接返回true表示对所有的事件都感兴趣
        // 可以通过相关逻辑设置对感兴趣的事件源进行订阅
        public boolean supportsSourceType(Class<?> sourceType) {
            return true;
        }
        // 数字越小,越先执行
        public int getOrder() {
            return 0;
        }
    }
  2. resources目录下创建文件

    image-20201010152343324.png

    spring.factories

    # Application Listeners
    org.springframework.context.ApplicationListener=\
    com.demo.listener.MyListener

1.2 Listener源码解析

  1. App.java

    public class App {
        public static void main(String[] args) {
    //        SpringApplication.run(App.class);
            // 这里的构造方法里面得到了所有的listener
            SpringApplication application = new SpringApplication(App.class);
            application.run(args);
        }
    }
  2. SpringApplication.java

    SpringApplication的构造方法,里面得到所有的listener

    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        // 一般传过来的 resourceLoader参数为空
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        // 推断 webApplicationType 的类型(比如servlet项目、普通java项目),这里很重要
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        // springboot最核心的地方。
        // 从 spring.factories 配置文件中定义的 ApplicationContextInitializer 类对应的value值保存到 initializers(List)集合中
        setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
        // 所有的listener都在这进行实例化了
        //  spring.factories 配置文件中定义的 ApplicationListener 类对应的value值保存到 listeners(List)集合中
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    
        // 推断启动类
        // 通过抛一个运行时异常,拿到所有的栈帧,从这里拿到 main()方法所在的主类。真流弊!!
        this.mainApplicationClass = deduceMainApplicationClass();
    }

类中的run()方法

public ConfigurableApplicationContext run(String... args) {
    // stopwatch 用于记录时间等
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();

    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    configureHeadlessProperty();
    // 从spring.factories 配置文件中拿到 key为 SpringApplicationRunListener 类的 value值
    // 这里就只有一个值: EventPublishingRunListener 类,在其构造方法中实例化了一个
    // SimpleApplicationEventMulticaster类,并在构造方法里面把所有的spring.factories文件中listener添加给了广播器
    SpringApplicationRunListeners listeners = getRunListeners(args);
    // 方法调用,点进去
    listeners.starting();
    // ......
}

getRunListeners()方法

private SpringApplicationRunListeners getRunListeners(String[] args) {
    Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
    // 就是添加一个 EventPublishingRunListener
    return new SpringApplicationRunListeners(logger,
                                             getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}

getSpringFactoriesInstances()方法

// 这个方法就是从spring.factories配置文件中 拿到指定类型(参数 type)的类的实例
// 比如:SpringApplicationRunListener、ApplicationContextInitializer
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
    ClassLoader classLoader = getClassLoader();
    // Use names and ensure unique to protect against duplicates

    // 比如:type就是 ApplicationContextInitializer类
    // loadFactoryNames()方法就是返回 在 spring.factories配置文件中定义的 ApplicationContextInitializer 类,
    // 的所有对应的value值
    Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));

    List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
    AnnotationAwareOrderComparator.sort(instances);
    return instances;
}
  1. spring.factories文件中

    image-20201010235722399.png

  2. SpringApplicationRunListeners.java

    SpringApplicationRunListeners构造方法

    // 这个构造方法的调用,在上面的getRunListeners()方法进行调用的
    SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners) {
        this.log = log;
        // 一般传过来的listeners就一个 EventPublishingRunListener
        this.listeners = new ArrayList<>(listeners);
    }
  3. EventPublishingRunListener.java

    // 这个构造方式的调用是在上面的SpringApplication类中的getSpringFactoriesInstances()方法中实例化的
    public EventPublishingRunListener(SpringApplication application, String[] args) {
        this.application = application;
        this.args = args;
        // 广播器,所有的事件发生后,都是这个广播器分发给对应的listener
        this.initialMulticaster = new SimpleApplicationEventMulticaster();
        // 把 SpringApplication类中的所有监听器添加到广播器中
        for (ApplicationListener<?> listener : application.getListeners()) {
            this.initialMulticaster.addApplicationListener(listener);
        }
    }

一个spring内部事件完整发布过程

一个spring容器的starting事件

  1. 首先在SpringApplication类中发布事件

    image-20201011001338585.png

  2. 然后这个调用就会进入SpringApplicationRunListener类中,调用其starting()方法

    image-20201011001412536.png

  3. 然后这个listener.starting()调用会进入EventPublishingRunListener类中,调用其starting()方法

    !image-20201011002357483.png

    上面的starting()方法,利用广播器,发布一个ApplicationStartingEvent事件

    image-20201011002507702.png

  4. 上面this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args))方法调用后,就会继续调用SimpleApplicationEventMulticaster类的multicastEvent()方法

    image-20201011003359632.png

上面方法调用的invokeListener()方法的内容如下:

image-20201011003728931.png

然后doInvokeListener(listener, event)方法继续调用如下,然后调用链就完毕

image-20201011003841381.png

上面multicastEvent()内部调用的getApplicationListeners(event, type),里面会调用如下的方法:

  1. 调用retrieveApplicationListeners()

    image-20201011004200181.png

  2. 调用supportsEvent()

    image-20201011004405305.png

  3. 最后调用supportsEvent()

    这里就是我们实现的listenre接口中的两个方法,只有都返回true才会成功的订阅

    image-20201011005159940.png

Last Modified: November 4, 2020