springboot源码分析(五)-监听器实现原理(下)

本文承接上文:springboot源码分析(五)-监听器实现原理(中)

开篇之前先把祖师爷搬出来
  费玉清:问大家一个脑筋急转弯,高个子女生,打一种化妆品,很常见的那种
      思考。。。
      思考。。。
      思考。。。
  揭晓谜底:唇膏
  反正谜底我已经揭晓了,至于大家能不能看到,我就不管了,哈哈
 

概述

上一篇文章已经把springboot中的监听器从源码的角度分析了一遍,有兴趣的可以去看一下,如果不想看源码,只是看一下如何自定义监听器,也可以一下这篇文章,这篇文章主要介绍一下自定义监听器的方法,以及如何让自定义的监听器生效,初次之外呢,还会介绍一下springboot在启动过程中都有哪些事件,执行的顺序是什么。

自定义监听器

通过实现ApplicationListener自定义监听器

@Order(1)
public class FirstApplicationListener implements ApplicationListener<ApplicationStartedEvent> {

    @Override
    public void onApplicationEvent(ApplicationStartedEvent applicationStartedEvent) {
        System.out.println("first, springboot start");
    }
}

这是第一个监听器,我们实现了ApplicationListener接口,并且在接口中我们指定了ApplicationStartedEvent作为我们感兴趣的事件,当springboot启动是,调用到 listeners.starting(); 的时候,就会被我们自定义的监听器监听到,会执行我们重写的onApplicationEvent方法。

我们还是老规矩,在resource目录下新建META-INF目录,之后在这个目录下新建spring.factories文件,在文件中加入如下配置项

org.springframework.context.ApplicationListener=com.example.demo.listener.FirstApplicationListener

等号后面是我们自定义的监听器的路径,大家根据自己的路径填写。

ok我们启动springboot项目,可以看到如下打印结果:

.   ____          _            __ _ _
 /\\ / ___‘_ __ _ _(_)_ __  __ _ \ \ \ ( ( )\___ | ‘_ | ‘_| | ‘_ \/ _` | \ \ \  \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  ‘  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.2.6.RELEASE)

firstApplicationContextInitializer is start
thirdApplicationContextInitializer is start
secondApplicationContextInitializer is start
2020-06-04 19:58:56.194  INFO 56577 --- [           main] com.example.demo.DemoApplication         : Starting DemoApplication on ganxinledeMacBook-Pro.local with PID 56577 (/Users/ganxinle/workspace/demo/target/classes started by ganxinle in /Users/ganxinle/workspace/demo)
2020-06-04 19:58:56.197  INFO 56577 --- [           main] com.example.demo.DemoApplication         : No active profile set, falling back to default profiles: default
2020-06-04 19:58:56.920  INFO 56577 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2020-06-04 19:58:56.929  INFO 56577 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2020-06-04 19:58:56.930  INFO 56577 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.33]
2020-06-04 19:58:56.987  INFO 56577 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2020-06-04 19:58:56.987  INFO 56577 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 752 ms
2020-06-04 19:58:57.115  INFO 56577 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService ‘applicationTaskExecutor‘
2020-06-04 19:58:57.243  INFO 56577 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ‘‘
2020-06-04 19:58:57.246  INFO 56577 --- [           main] com.example.demo.DemoApplication         : Started DemoApplication in 1.328 seconds (JVM running for 1.662)
first, springboot start

说明我们的配置成功了,但是有的小伙伴可能有疑问,为什么是最后打印的,这个有兴趣的小伙伴自己去探究,我没有深究??

通过实现SmartApplicationListener自定义监听器

@Order(1)
public class SecondApplicationListener implements SmartApplicationListener {
    @Override
    public boolean supportsEventType(Class<? extends ApplicationEvent> aClass) {
        if (ApplicationStartedEvent.class.isAssignableFrom(aClass)){
            return true;
        }
        return false;
    }

    @Override
    public void onApplicationEvent(ApplicationEvent applicationEvent) {
        System.out.println("second,springboot start");
    }
}

我们实现了里面的两个方法,第一个方法就是在上一篇文章讲的,当寻找对当前事件感兴趣的监听器的时候,会调用这个方法来判断,我们在if中使用了isAssignableFrom方法来判断当前事件是不是ApplicationStartedEvent事件的字类或者是他本身,对这个方法不太熟悉的,我会在附录中把解释这个的文章的链接贴出来,大家看一下就明白了。

我们修改springboot的启动类,把这个监听器加入到监听器列表中

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(DemoApplication.class);
        application.addListeners(new SecondApplicationListener());
        application.run(args);
    }
}

至于为什么这样可以生效,大家可以看一下上一篇文章,我这里就不重复了。

??,我们启动springboot项目

.   ____          _            __ _ _
 /\\ / ___‘_ __ _ _(_)_ __  __ _ \ \ \ ( ( )\___ | ‘_ | ‘_| | ‘_ \/ _` | \ \ \  \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  ‘  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.2.6.RELEASE)

firstApplicationContextInitializer is start
thirdApplicationContextInitializer is start
secondApplicationContextInitializer is start
2020-06-04 19:58:56.194  INFO 56577 --- [           main] com.example.demo.DemoApplication         : Starting DemoApplication on ganxinledeMacBook-Pro.local with PID 56577 (/Users/ganxinle/workspace/demo/target/classes started by ganxinle in /Users/ganxinle/workspace/demo)
2020-06-04 19:58:56.197  INFO 56577 --- [           main] com.example.demo.DemoApplication         : No active profile set, falling back to default profiles: default
2020-06-04 19:58:56.920  INFO 56577 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2020-06-04 19:58:56.929  INFO 56577 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2020-06-04 19:58:56.930  INFO 56577 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.33]
2020-06-04 19:58:56.987  INFO 56577 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2020-06-04 19:58:56.987  INFO 56577 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 752 ms
2020-06-04 19:58:57.115  INFO 56577 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService ‘applicationTaskExecutor‘
2020-06-04 19:58:57.243  INFO 56577 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ‘‘
2020-06-04 19:58:57.246  INFO 56577 --- [           main] com.example.demo.DemoApplication         : Started DemoApplication in 1.328 seconds (JVM running for 1.662)
first, springboot start
second,springboot start

可以发现第二个也顺利打印出来了。

以上我只列举了两种把自定义监听器加入到系统的中方法,其实还有别的方法,我这里就不介绍了,知道这两种我觉得就够了。

springboot在启动过程中都执行了哪些事件,顺序是什么

springboot源码分析(五)-监听器实现原理(下)

从启动开始分别执行的事件如下:

ApplicationStartingEvent -> ApplicationEnvironmentPreparedEvent -> ApplicationContextInitializedEvent -> ApplicationPreparedEvent -> ApplicationStartedEvent -> ApplicationReadyEvent

启动失败执行ApplicationFailedEvent事件

springboot源码分析(五)-监听器实现原理(下)

这些个事件基本都是SpringApplicationEvent的子类。

总结

基本上来说如果看过上一篇文章之后,对于这一篇介绍的自定义监听器应该很容易理解,至于后面的执行的事件的流程那个东东,需要对整个springboot的启动流程有一个很清楚的了解,这个现在可以先了解一下,以后会慢慢分享。

番外话:监听器系列写完了,终于知道springboot中是怎么使用设计模式的了,以后如果自己实现什么大型一点的系统,就可以仿照这个玩意也搞一个监听器模式,??

相关推荐