SpringBoot 监听器加载过程
首先加载 META-INF/spring.factories
文件中的 key:ApplicationListener,value:全限定类名
,然后获取到所有的实例存入 ApplicationContext 中,方便后续创建多播器对象时可以获取到这些监听器实例,注入:ApplicationContext 实例以后,就可以调用它来进行事件发布动作
通过观察以上内置监听器的源码,可以发现在这些监听器里都实现了 ApplicationListener
同时会使用到抽象类 ApplicationEvent
,但这些内置监听器中的事件都是基于它抽象的实现,定义自己的事件类
事件驱动机制是基于观察者设计模式的实现
,通过 ApplicationEvent 类、ApplicationListener 接口,可以通过 ApplicationContext 实现事件的发布
如下图整理了 SpringBoot 内置的监听器和事件:
# Run Listeners org.springframework.boot.SpringApplicationRunListener=\ org.springframework.boot.context.event.EventPublishingRunListener
前戏: 在事件准备发布时,首先会通过 getRunListeners 方法来获取我们在 META-INF/spring.factories
文件中的定义 SpringApplicationRunListener 接口实现类 EventPublishingRunListener
,同时会完成该类的实例化操作,调用构造方法时,会初始化多路广播器对象:SimpleApplicationEventMulticaster
,同时从上下文获取到前面加载好的 11 个监听器,进行绑定操作.
前戏工作做完以后,就到了发布事件的时候了
SimpleApplicationEventMulticaster#multicastEvent:在容器启动时会调用 listener#starting
方法(事件名:ApplicationStartingEvent)、环境对象准备前会调用 listener#environmentPrepared
(事件名:ApplicationEnvironmentPreparedEvent)
以 ConfigFileApplicationListener 为例,会在其内部处理配置文件的解析工作,接受 ApplicationEnvironmentPreparedEvent 事件的处理,源码如下:
public void onApplicationEvent(ApplicationEvent event) { if (event instanceof ApplicationEnvironmentPreparedEvent) { this.onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent)event); } if (event instanceof ApplicationPreparedEvent) { this.onApplicationPreparedEvent(event); } }
自定义监听器扩展实现
通过几个不同类型自定义事件案例来加深对事件驱动机制的理解
监听所有事件
先创建一个自定义监听器,来监听所有的事件;创建一个 Java 类,实现 ApplicationListener 接口在泛型中指定要监听的事件类型即可,如果要监听所有的事件,那么泛型就写 ApplicationEvent
public class MySpringApplicationListener implements ApplicationListener<ApplicationEvent> { @Override public void onApplicationEvent(ApplicationEvent event) { System.out.println("自定义监听器--->" + event); } }
之后为了在容器启动中能够发现我们的监听器并且添加到 SimpleApplicationEventMulticaster 中,我们需要在 spring.factories/META-INF
中注册自定义的监听器
org.springframework.context.ApplicationListener=\ com.vnjohn.demo.listener.MySpringApplicationListener
这样当我们启动服务的时候就可以看到相关事件发布,我们的监听器被触发了,会打印对应的信息
监听特定事件
如果是监听特定的事件,我们只需要在泛型出指定类型即可
public class MySpringApplicationStartingListener implements ApplicationListener<ApplicationStartingEvent> { @Override public void onApplicationEvent(ApplicationStartingEvent event) { // 该事件是启动启动时就会进行发布的,查询容器启动日志信息即可 System.out.println("MySpringApplicationStartingListener--------->" + event); } }
org.springframework.context.ApplicationListener=\ com.vnjohn.demo.listener.MySpringApplicationListener,\ com.vnjohn.demo.listener.MySpringApplicationStartingListener
启动服务时可以看到相关的事件发布
自定义事件
若我们想要通过自定义的监听器来监听自定义的事件呢?首先创建自定义的事件类,非常简单,只需要继承 ApplicationEvent 即可
public class MyEvent extends ApplicationEvent { public MyEvent(Object source) { super(source); } }
然后在自定义的监听器中监听自定义的事件
public class MyCustomerEventListener implements ApplicationListener<MyEvent> { @Override public void onApplicationEvent(MyEvent event) { System.out.println("MyCustomerEventListener ---》自定义事件触发" + event); // 触发对应的事件后 业务处理 new Thread(()->{ // 业务.... }).start(); } }
事件的监听和发布是同步执行的,如果想让其异步的进行,可以抛给一个线程进行处理
org.springframework.context.ApplicationListener=\ com.vnjohn.demo.listener.MySpringApplicationListener,\ com.vnjohn.demo.listener.MySpringApplicationStartingListener,\ com.vnjohn.demo.listener.MyCustomerEventListener
之后我们就可以在我们特定的业务场景中类发布对应的事件了
@Component public class MyApplicationContextAware implements ApplicationContextAware { private static ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { MyApplicationContextAware.applicationContext = applicationContext; } public static void publishEvent(ApplicationEvent applicationEvent) { applicationContext.publishEvent(applicationEvent); } }
@RestController public class HelloController { @GetMapping("/hello") public String hello(){ MyApplicationContextAware.publishEvent(new MyEvent(new Object())); return "hello---"; } }
当提交请求后,对应的监听器就触发了,这样一来不光搞清楚了 SpringBoot 中的监听机制,而且也可以扩展使用到我们业务开发中了
总结
希望对你有所帮助,您的支持是对我最大的鼓励,关注+赞+收藏三连,感谢!
如果觉得博文不错,关注我 vnjohn,后续会有更多实战、源码、架构干货分享!
大家的「关注❤️ + 点赞👍 + 收藏⭐」就是我创作的最大动力!谢谢大家的支持,我们下文见!