SpringBoot源码初学者(二):SpringBoot事件监听器
2020/3/30 17:02:20
本文主要是介绍SpringBoot源码初学者(二):SpringBoot事件监听器,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
ps:真正适合阅读源码的新手来看的SpringBoot源码讲解,如果你真的想读懂SpringBoot源码,可以按照以下推荐的方式来阅读文章
- 打开ide,打开SpringBoot源码,跟着文章一起写注释,写自己的注释
- 不要过于纠结没讲到的地方,毕竟SpringBoot源码那么多,想全讲完是不可能的,只要跟着文章认真阅读,SpringBoot是如何运行的一定可以有一个较为深刻的理解
- 文章适合通篇阅读,不适合跳读,跳跃性的阅读很容易错过重要的东西
- 同样的如果之前的文章没有读过,还是最好先去看之前的文章
- 阅读源码必然少不了大段大段的源码,一定要耐心,不要翻翻了事,往往是那些最长的方法中才是真正需要学习的
- 如果断更了请用点赞、收藏、评论的方式激励我
系列文章链接: 《SpringBoot源码初学者(一):SpringBoot功能扩展接口的使用与源码分析》
一、监听器模式
在学习的路上遵循一些原则,可以更高效的学习,其中就有这么一条“循循渐进”,在深入SpringBoot之前先要了解清楚什么是监听器,监听器是如何实现的,这些都是对付大魔王的神兵利器,和RPG游戏一样打boss之前先要打小怪提升等级,爆出“屠龙宝刀”。 伊泽瑞尔作为瓦罗拉大陆上组名的探险家在探险的路上,却总是受到天气的影响无法冒险,所以他拜托我帮他写一个软件,辅助他关注天气。
1、监听器模式小demo!天气监听器
步骤1:创建抽象类WeatherEvent(天气状态)
public abstract class weatherEvent{ //获取天气状态 public abstract String getWeather(); } 复制代码
步骤2:实现下雪和下雨事件 下雪事件
public class SnowEvent extends WeatherEvent{ @Overide public String getWeather(){ return "下雪了"; } } 复制代码
下雨事件
public class RainEvent extends WeatherEvent{ @Overide public String getWeather(){ return "下雨了"; } } 复制代码
步骤3:创建天气监听器接口
public interface WeatherListener{ void onWeatherEvent(WeatherEvent event); } 复制代码
步骤4:实现监听器,分别处理下雪和下雨的天气 下雪的时候需要穿上大棉袄,带上手套御寒
public class SnowListener implements WeatherListener{ @Override public void onWeatherEvent(WeatherEvent event){ if(event instanceof SnowEvent){ event.getWeather(); System.out.println("今天下雪!请增加衣物,做好御寒保护!"); } } } 复制代码
下雨的时候需要带雨伞,穿雨鞋
public class RainListener implements WeatherListener{ @Override public void onWeatherEvent(WeatherEvent event){ if(event instanceof RainEvent){ event.getWeather(); System.out.println("今天下雨!出门请带好雨伞"); } } } 复制代码
步骤5:创建广播器接口
public interface EventMulticaster{ //广播事件 void multicastEvent(WeatherEvent event); //添加监听器 void addListener(WeatherListener weaterListener); //删除监听器 void removeListener(WeatherListener weaterListener); } 复制代码
步骤6:抽象类实现广播接口
public abstract class AbstractEventMulticaster implements EventMulticaster{ //存放监听器的集合,所有需要监听的事件都存在这里 private List<WeaterListener> listenerList = new ArrayList<>(); @Override public void multicastEvent(WeatherEvent event){ //采用模板方法,子类可以实现的doStart和doEnd,在调用监听器之前和之后分别作出扩展 //SpringBoot中有着大量相似的操作 //SpringBoot中的前置处理器和后置处理器,就是这样实现的 doStart(); //循环所有调用所有监听器的onWeatherEvent方法 listenerList.forEach(i -> i.onWeatherEvent(evnet)); doEnd(); } @Override public void addListener(WeatherListener weaterListener){ listenerList.add(weaterListener); } @Override public void removeListener(WeatherListener weaterListener){ listenerList.remove(weaterListener); } abstract void doStart(); abstract void doEnd(); } 复制代码
步骤7:实现天气事件的广播
public class WeatherEventMulticaster extends AbstractEventMulticaster{ @Override void doStart(){ System.out.println("开始广播天气预报!"); } @Override void doEnd(){ System.out.println("广播结束!Over!"); } } 复制代码
步骤8:测试并触发广播
public class Test{ public static void main(String[] args){ //创建广播器 WeatherEventMulticaster eventMulticaster = new WeatherEventMulticaster(); //创建监听器 RainListener rainListener = new RainListener(); SnowListener snowListener = new SnowListener(); //添加监听器 eventMulticaster.addListener(rainListener); eventMulticaster.addListener(snowListener); //触发下雨事件 eventMulticaster.multicastEvent(new RainEvent()); //除非下雪事件 eventMulticaster.multicastEvent(new SnowEvent()); } } 复制代码
2、黑默丁格大讲堂,监听器模式机制讲解
伊泽瑞尔的探险活动终于不再受到天气的骚扰了,可是他并不明白小小的玩意为什么如此神奇,多次询问过我,可是无赖我语言贫乏,无法将如此复杂的思想表达清楚,只要求助老友黑默丁格,帮忙说明。
ps:工作中不仅要能实现功能,还要注重表达能力,在面试的时候能把思想表达的清楚可以拿到更高的薪资,在和测试交流的时候可以帮助测试理解实现原理,测试出隐藏在深处的bug,当然作为天才程序员的大伙是没有bug的,肯定是环境问题或者操作不当导致的。
黑默丁格拿到代码,简单看了两眼就分析出了各个模块的作用:
- 事件:步骤1和步骤2,通过对天气进行抽象,并实现下雨和下雪的天气状态
- 监听器:步骤3和步骤4,规范对天气监听的模式,并且规范对应天气下,需要如何处理
- 广播器:步骤5、步骤6和步骤7,当有事件发生的时候,广播器发出信号,告知所有的监听器,监听器根据事件作出相应的处理。触发下雨事件的时候,下雨监听器收到消息,它抬头一看乌云密布电闪雷鸣,微微一愣,大喊一句:“打雷下雨收衣服啊!!”,广播器继续通知下一个监听器下雪监听器,下雪监听器看看天空,摆摆手,说:“这事与我无关去找别人”
- 触发机制:步骤8,demo中采用的硬编码的形式触发的,在实际运用中,可能是湿度仪检测到湿度暴涨开始下雨了,触发广播。
在23种设计模式中是没有监听器模式的,监听器模式是观察者模式的一种实现,这两个名字都容易让人产生一些误导,在“监听”、“观察”很容易让人觉得是监听器发现了事件,然后行动。实际上是广播器把事件推送给所有的监听器,每个监听器都对事件做出判断和处理。
二、SpringBoot事件监听器的实现
1、ApplicationListener接口
ApplicationListener是Spring事件机制的一部分,与抽象类ApplicationEvent类配合来完成ApplicationContext的事件机制,实现ApplicationListener接口的类,会在SpringBoot加入到广播器中,当ApplicationContext触发了一个事件,就用广播器通知所有实现ApplicationListener接口的类。
//这个注解表示,当前类只有一个方法 @FunctionalInterface //传入的泛型,说明这个监听器,需要监听的事件类型 //继承的EventListener类,是个空类,主要是声明继承它的类是个事件监听器,面向对象编程的思想体现 public interface ApplicationListener<E extends ApplicationEvent> extends EventListener { /** * Handle an application event. * @param event the event to respond to */ void onApplicationEvent(E event); } 复制代码
不难发现ApplicationListener的接口与我们实现的天气监听器的步骤3几乎一样,如果理解了小demo这个类的作用肯定已经了解的明明白白。
2、ApplicationEventMulticaster接口
ApplicationEventMulticaster是Spring事件机制的广播器接口,所有的广播器都需要实现此接口,主要作用是管理所有的监听器,以及推送事件给监听器。
public interface ApplicationEventMulticaster { //添加一个监听器 void addApplicationListener(ApplicationListener<?> listener); //根据beanName添加一个监听器 void addApplicationListenerBean(String listenerBeanName); //移除一个监听器 void removeApplicationListener(ApplicationListener<?> listener); //根据beanName移除一个监听器 void removeApplicationListenerBean(String listenerBeanName); //移除所有监听器 void removeAllListeners(); //广播事件的方法 void multicastEvent(ApplicationEvent event); void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType); } 复制代码
3、SpringBoot的7大事件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cu6pwkNo-1585491460589)(en-resource://database/2523:1)]
- EventObject:事件顶级对象,所有事件对象的根对象
- ApplicationEvent:应用事件
- SpringApplicationEvent:Spring自己的事件,Spring框架自身的事件都会实现这个接口
- ApplicationStartingEvent:启动事件,框架刚刚启动就会发出这个事件
- ApplicationEnvironmentPreparedEvent:环境在变完成,系统属性和用户指定已经加载完成
- ApplicationContextInitializedEvent:已经创建好了上下文,并且还没有加载任何bean之前发出这个事件
- ApplicationPreparedEvent:在Bean定义开始加载之后,尚未完全加载之前,刷新上下文之前触发
- ApplicationStartedEvent:bean已经创建完成,上下文已经刷新完成,但是ApplicationRunner和CommandLineRunne两个扩展接口并未执行
- ApplicationReadyEvent:ApplicationRunner和CommandLineRunne两个扩展接口执行完成之后触发
- ApplicationFailedEvent:在启动发生异常时触发
(1)事件发生顺序
启动 —》ApplicationStartingEvent —》ApplicationEnvironmentPreparedEvent —》ApplicationContextInitializedEvent —》 ApplicationPreparedEvent —》ApplicationStartedEvent —》 ApplicationReadyEvent —》启动完毕
中间发生异常 —》ApplicationFailedEvent —》启动失败
4、事件监听器的源码分析
(1)监听器注册流程
如果看过之前的文章
《 SpringBoot源码初学者(一):SpringBoot功能扩展接口的使用与源码分析》:https://juejin.im/post/5e8179db6fb9a03c563bfd3d
这里就很容易理解,不想完整的阅读可以只看一下工厂加载机制源码解析
的部分
与ApplicationContextInitializer接口完全一样的流程进行注册的,只是把ApplicationContextInitializer接口
换成了ApplicationListener接口
我们还是从最开始的main方法一步步看。 步骤1:查看SpringBoot启动类
@SpringBootApplication public class Application { public static void main(String[] args) { //进入run方法的源码 SpringApplication.run(Application.class, args); } } 复制代码
步骤2:这里可以看到一层简单的调用
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) { //进入这个同名方法,继续戳run方法 return run(new Class<?>[] { primarySource }, args); } 复制代码
步骤3:这里就比较有意思了,注意一下注释
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { //点这个SpringApplication构造方法 return new SpringApplication(primarySources).run(args); } 复制代码
步骤4:没有什么用的封装,对构成函数复用
public SpringApplication(Class<?>... primarySources) { //点this,查看构造函数 this(null, primarySources); } 复制代码
步骤5:
这里我们可以看到两个熟悉的名字getSpringFactoriesInstances方法和ApplicationContextInitializer接口
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); this.webApplicationType = WebApplicationType.deduceFromClasspath(); //这里就是上一篇文章说的ApplicationContextInitializer接口注册 setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class)); //这里就是ApplicationListener注册的位置,可以看出主要区别就是查询的接口类不同 //setListeners是找到的对象存到容器中,存到一个list属性中,方便以后使用 //这个存放对象的list,对应的是小demo的AbstractEventMulticaster类中list,作用是一样一样的 //getSpringFactoriesInstances方法详解参考文章《SpringBoot功能扩展接口的使用与源码分析》 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = deduceMainApplicationClass(); } 复制代码
(2)监听器触发流程
步骤1:查看SpringBoot启动类
@SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } 复制代码
步骤2:ConfigurableApplicationContext类
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) { return run(new Class<?>[] { primarySource }, args); } 复制代码
步骤3:这次进入run方法
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { //点击run方法 return new SpringApplication(primarySources).run(args); } 复制代码
步骤4:每次看到这个方法,都感觉它罪孽深重,多少人从它开始看起,踏上阅读源码的不归路
代码较长,这次就不写所有的注释了,具体注释看这里https://juejin.im/post/5e8179db6fb9a03c563bfd3d
public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); configureHeadlessProperty(); //获取事件运行器 //SpringApplicationRunListeners内部包含一个SpringApplicationRunListener(这里s没有了)的集合 //SpringApplicationRunListener有7大事件的执行方法,在对应的地点会被调用,SpringBoot通过这个实现事件的触发 //SpringBoot自带一个实现,这个实现分别会执行定义好的7大事件 //使用者可以通过实现SpringApplicationRunListener的接口,定义在对应事件所需执行的命令 //总体流程还是很简单的,留给大家自己阅读 SpringApplicationRunListeners listeners = getRunListeners(args); //监听器的故事从这里开始,我们这次的故事也从这里起航 //进入starting方法 listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment); Banner printedBanner = printBanner(environment); context = createApplicationContext(); exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); prepareContext(context, environment, listeners, applicationArguments, printedBanner); refreshContext(context); afterRefresh(context, applicationArguments); stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); } listeners.started(context); callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); } try { listeners.running(context); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, null); throw new IllegalStateException(ex); } return context; } 复制代码
步骤5:没有千层套路
public void starting() { //listeners里面存放了所有的SpringApplicationRunListener(事件触发器) for (SpringApplicationRunListener listener : this.listeners) { //循环执行事件触发器的starting方法 //点击进入看看SpringBoot自带的事件触发器是如何运行的 listener.starting(); } } 复制代码
步骤6:广播器发送事件
@Override public void starting() { //initialMulticaster是广播器 this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args)); } 复制代码
步骤7:广播器发送事件
@Override public void starting() { //initialMulticaster是广播器 //进入multicastEvent方法 this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args)); } 复制代码
步骤8:广播事件的时候要判断这个事件的类型,判断需不需要在这个时间点执行
@Override public void multicastEvent(ApplicationEvent event) { //resolveDefaultEventType方法,解析事件的默认类型 //进入resolveDefaultEventType方法,步骤9 //进入multicastEvent方法,步骤11 multicastEvent(event, resolveDefaultEventType(event)); } 复制代码
步骤9:获取事件类型
private ResolvableType resolveDefaultEventType(ApplicationEvent event) { //获取事件类型 //进入forInstance方法,步骤10 return ResolvableType.forInstance(event); } 复制代码
步骤10:通过接口判断时间类型
public static ResolvableType forInstance(Object instance) { //断路判断,如果instance是个空,就停止SpringBoot的启动,并报错 Assert.notNull(instance, "Instance must not be null"); //判断有没有实现ResolvableTypeProvider这个接口 //ResolvableTypeProvider接口,表明这个类的事件类型可以被解析 if (instance instanceof ResolvableTypeProvider) { //强转成ResolvableTypeProvider类型,然后获取事件类型 ResolvableType type = ((ResolvableTypeProvider) instance).getResolvableType(); if (type != null) { //事件类型不为空,就直接返回 return type; } } //返回一个默认类型,传进来的instance是什么类型,就把这个类型包装成ResolvableType,然后返回 //返回步骤8 return ResolvableType.forClass(instance.getClass()); } 复制代码
步骤11:
开始广播
两个参数:event:需要执行的事件 eventType:事件的类型
@Override public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) { //如果事件类型为空,执行resolveDefaultEventType方法(步骤9和步骤10) ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event)); //获取任务的执行的线程池 //如果没有特别指定,返回为null,SpringBoot这里就是空的 Executor executor = getTaskExecutor(); //getApplicationListeners方法,获取对这个事件感兴趣的监听器 //点击进入getApplicationListeners方法,进入步骤12 for (ApplicationListener<?> listener : getApplicationListeners(event, type)) { if (executor != null) { //在指定线程上执行触发 executor.execute(() -> invokeListener(listener, event)); } else { //默认方式执行触发 invokeListener(listener, event); } } } 复制代码
步骤12:
获取对这个事件感兴趣的监听器(缓存获取逻辑)
参数说明:
event:当前发生的事件,这个方法就是找到对这个事件感兴趣的监听器
eventType:事件类型
protected Collection<ApplicationListener<?>> getApplicationListeners( ApplicationEvent event, ResolvableType eventType) { //获取事件发生的源头类,这里就是SpringApplication Object source = event.getSource(); //获取原头类的类型 Class<?> sourceType = (source != null ? source.getClass() : null); //获取缓存的key ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType); //快速执行,从缓存中获取监听器,如果这个方法已经执行了过了,就不要在获取一次了,直接拿到缓存 ListenerRetriever retriever = this.retrieverCache.get(cacheKey); if (retriever != null) { //返回对当前事件感兴趣的监听器 return retriever.getApplicationListeners(); } if (this.beanClassLoader == null || (ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) && (sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) { //通过key上锁,这是上锁的一个很有效的方式,定义一个属性作为锁的key synchronized (this.retrievalMutex) { //上锁之后再次检查,有没有其他地方触发了当前事件,把监听器的列表放入了缓存中 //写过双层验证的单例模式对这里不会陌生,主要原理是一样的 retriever = this.retrieverCache.get(cacheKey); if (retriever != null) { //返回对当前事件感兴趣的监听器 return retriever.getApplicationListeners(); } retriever = new ListenerRetriever(true); //真正的查找逻辑被封装在这里 //SpringBoot这种千层套路,是有规律可循的,这一次是缓存的封装,下一次是实际的调用 //我们编程的时候可以学习一下,比如封装缓存的查询,再去数据库,降低耦合度 //点retrieveApplicationListeners方法进入 步骤13 Collection<ApplicationListener<?>> listeners = retrieveApplicationListeners(eventType, sourceType, retriever); //存入缓存中 this.retrieverCache.put(cacheKey, retriever); return listeners; } } else { //不需要加锁的,并且不需要缓存的查询方式 //这个方法中有两处调用了retrieveApplicationListeners方法,在方法的内部对有无缓存,做了不同的处理 //个人观点:应该把内部的缓存逻辑移到这层中,否则耦合度依旧很高 return retrieveApplicationListeners(eventType, sourceType, null); } } 复制代码
步骤13
:真正获取监听器的逻辑
private Collection<ApplicationListener<?>> retrieveApplicationListeners( ResolvableType eventType, @Nullable Class<?> sourceType, @Nullable ListenerRetriever retriever) { List<ApplicationListener<?>> allListeners = new ArrayList<>(); Set<ApplicationListener<?>> listeners; Set<String> listenerBeans; synchronized (this.retrievalMutex) { //获取所有的监听器实例 listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners); //获取所有监听器的beanName listenerBeans = new LinkedHashSet<>(this.defaultRetriever.applicationListenerBeans); } //对所有的监听器进行逐一的循环 for (ApplicationListener<?> listener : listeners) { //判断监听器是否对这个事件感兴趣 //点击supportsEvent方法进入 步骤14 if (supportsEvent(listener, eventType, sourceType)) { if (retriever != null) { //如果监听器功能开启了缓存,就存到缓存中 retriever.applicationListeners.add(listener); } //不管有没有缓存都会存到这里 allListeners.add(listener); } } //通过工厂方式,获取监听器,一般情况不会走这里 if (!listenerBeans.isEmpty()) { //获取bean工厂 BeanFactory beanFactory = getBeanFactory(); //循环监听器beanName for (String listenerBeanName : listenerBeans) { try { //更具beanName,获取监听器的类型 Class<?> listenerType = beanFactory.getType(listenerBeanName); // 判断监听器是否对这个事件感兴趣 if (listenerType == null || supportsEvent(listenerType, eventType)) { //获取bean实例,这个方法写作getBean,读作createBean //这是ioc中非常重要的一块逻辑,当获取不到bean的时候,就会创建一个bean对象 //具体的我们在后续ioc源码分析的时候讲解 ApplicationListener<?> listener = beanFactory.getBean(listenerBeanName, ApplicationListener.class); if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) { //也是判断是否有缓存的逻辑 if (retriever != null) { //多一个判断是否单例的逻辑 if (beanFactory.isSingleton(listenerBeanName)) { retriever.applicationListeners.add(listener); } else { //原形bean这里,想起来以前有个组员说这个叫“多例”,最好还是叫“原型” retriever.applicationListenerBeans.add(listenerBeanName); } } allListeners.add(listener); } } } catch (NoSuchBeanDefinitionException ex) { } } } //进行排序,SpringBoot的常规操作了,根据Order接口或者注解进行排序 AnnotationAwareOrderComparator.sort(allListeners); //对缓存进行一次刷新,把以前的结果清空,将这次运行的结果缓存 if (retriever != null && retriever.applicationListenerBeans.isEmpty()) { retriever.applicationListeners.clear(); retriever.applicationListeners.addAll(allListeners); } //返回获取到的监听器 //返回 步骤12 return allListeners; } 复制代码
步骤14
:判断监听器是否对当前事件感兴趣
protected boolean supportsEvent( ApplicationListener<?> listener, ResolvableType eventType, @Nullable Class<?> sourceType) { //判断监听器,是否是GenericApplicationListener的子类 //starting的事件并不是其子类 //GenericApplicationListener使用了装饰器模式 //著名的装饰器模式是java中io流(inputStream这些) //GenericApplicationListener中可以解析ApplicationListener接口中的泛型参数,接口如下: //“ApplicationListener<E extends ApplicationEvent>”要是还想不起来,回头看一下上面小Demo中的使用,和对这个接口的介绍 GenericApplicationListener smartListener = (listener instanceof GenericApplicationListener ? (GenericApplicationListener) listener : new GenericApplicationListenerAdapter(listener)); //下面就变得简单了,虽然内部的判断很繁杂,总体只做了两件事情 //supportsEventType:判断监听器是否支持当前事件 //supportsSourceType:监听器是否对这个事件的发起来类感兴趣 //返回一个总的bool值,返回 步骤13 return (smartListener.supportsEventType(eventType) && smartListener.supportsSourceType(sourceType)); } 复制代码
5、自定义SpringBoot监听器
(1)通过spring.factories注入
步骤1:创建监听器,并实现ApplicationListener接口
//我们让这个监听器对ApplicationStartedEvent事件感兴趣 @Order(1) public class TestListener implements ApplicationListener<ApplicationStartedEvent>{ @Ovrride public void onApplicationEvent(ApplicationStartedEvent event){ System.out.println("hello, Application start is over"); } } 复制代码
步骤2:在spring.factories中添加实现类的指引
这里涉及上一讲的内容,还不会的小伙伴们猛戳这里,赶紧补习一下:
https://juejin.im/post/5e8179db6fb9a03c563bfd3d
#com.gyx.test.Listener是刚刚写的监听器的全路径名 org.springframework.context.ApplicationListener=com.gyx.test.TestListener 复制代码
然后运行程序,就可以发现打印的语句出现了
(2)SpringApplication手动注入
步骤1:创建监听器,并实现ApplicationListener接口,和上面的完全一样 步骤2:修改SpringBoot启动类
@SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication springApplication = new SpringApplication(Application.class); //添加到初始化配置项中 springApplication.addListeners(new TestListener()); springApplication.run(args); } } 复制代码
(3)SpringBoot的配置文件中注册
步骤1:创建监听器,并实现ApplicationListener接口,和上面的完全一样 步骤2:修改配置文件
context.listener.classes=com.gyx.test.TestListener 复制代码
看过上一课的小伙伴们,是不是发现了,和之前ApplicationContextInitializer的注册方式完全一样!!!是不是有点感觉了,趁热打铁赶紧吧上一讲再去回顾一下吧
(4)多事件监听,实现SmartApplicationListener接口
这种方法只是实现的接口不一样,注入的方式是一样的,上面的三种注入方式都可以使用 步骤1:创建监听器,并实现SmartApplicationListener接口
@Order(1) public class TestSmartListener implements SmartApplicationListener{ @Ovrride public boolean supportsEventType(Class<? extends ApplicationEvent> eventType){ //这里是类型判断,判断监听器感兴趣的事件 //可以对多个事件感兴趣,这里就配置了两个事件 return ApplicationStartedEvent.class.isAssignableFrom(eventType) || ApplicationPreparedEvent.class.isAssignableFrom(eventType); } @Ovrride public void onApplicationEvent(ApplicationStartedEvent event){ System.out.println("hello, This is smartApplicationListener"); } } 复制代码
这篇关于SpringBoot源码初学者(二):SpringBoot事件监听器的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-12-26JavaScript入门教程:从零开始学习JavaScript编程
- 2024-12-26JavaScript入门教程:从零开始学习JavaScript
- 2024-12-26JS编程入门指南:从零开始学习JavaScript
- 2024-12-25Java编程面试题详解与解答
- 2024-12-25TS基础知识详解:初学者必看教程
- 2024-12-252024面试题解析与攻略:从零开始的面试准备指南
- 2024-12-25数据结构与算法学习:新手入门教程
- 2024-12-25初学者必备:订单系统资料详解与实操教程
- 2024-12-24内网穿透资料入门教程
- 2024-12-24微服务资料入门指南