首先要说明的是Spring-boot的事件通知机制并不是Spring-boot的功能,而是Spring具有的功能。
0x00、使用篇
从用法上讲是很简单的,一般分为三步
一、定义事件源
创建一个事件源类继承ApplicationEvent这个类,这个类构造函数默认有个 source 的入参数,用来方便事件传参数,定义一个事件类的代码类似如下:
import org.springframework.context.ApplicationEvent; /** * Created by 席有芳 on 2018/7/7. * * @author 席有芳 */ public class StartedEvent extends ApplicationEvent { /** * Create a new ApplicationEvent. * * @param source the object on which the event initially occurred (never {@code null}) */ public StartedEvent(Object source) { super(source); } }
二、定义事件监听器
首先要注意的是事件监听器是Spring的一个bean,所以一定要加上@Component这个注解或者类似的定义bean的注解,在需要处理事件的方法上加上@EventListener的注解并指定事件源,大致代码如下:
@Component public class ShowUIEventListener { @EventListener(StartedEvent.class) public void showUI(StartedEvent startedEvent) { //具体业务逻辑 } }
一个事件源可以绑定多个事件监听器,只要在不同的bean方法上加上同样的 @EventListener 注解就行,可以通过加上 @Order 注解控制监听器的执行顺序。可以通过 event.getSource() 获取到构造函数时候指定的对象。
三、发布并执行事件
通过 applicationContext.publishEvent(event); 的方式发布一个事件,事件发布完成后会自动触发并执行相关的EventListener 。
使用方法就是这么简单。然后我们来具体分析spring的源码实现。
0x01、原理篇
首先根据publishEvent的方法可以发现 Spring的ApplicationEventPublisher.java这个接口定一个了 publishEvent 相关的方法。追踪源码发现具体实现在 AbstractApplicationContext.java 这个类中。这个类中会通过 ApplicationEventMulticaster 来广播事件,具体代码如下:
源码很清晰,同时也可以发现如果是SpringMVC这种子容器定义的事件也会同步广播到父容器中。
接着看看广播器的源码
主要就两个部分,第一部分根据事件源提取出事件类型,第二步根据事件源与事件类型获取到对应的bean 并执行相关的方法。
getApplicationListeners 这个方法在 AbstractApplicationEventMulticaster 类中实现,具体逻辑大致是 首先去缓存里获取如果获取不到则调用retrieveApplicationListeners方法获取。
getApplicationListeners方法如下:
retrieveApplicationListeners方法如下:
代码很明显,容器的bean中找出满足规则的Listener组成一个列表,并放到缓存里。