上一篇我们介绍了SpringFactoriesLoader。这一篇我接着来介绍一下Spring的另一个知识点,就是Spring容器的事件监听机制。
说到事件,我们第一反应是什么是事件?其实 事件是发生在应用程序中的动作,比如点击按钮,在文本框中输入内容等操作都被称为事件。而当事件触发时,应用程序做出的一定的响应则表示应用监听了这个事件,而在服务器端,事件的监听机制更多的用于异步通知以及监控和异常处理。Java提供了实现事件监听机制的两个基础类:自定义事件类型扩展自java.util.EventObject
,事件的监听器扩展自java.util.EnventListener
。下面我们就以一个监控方法的耗时为例。
首先自定义事件类型,通常的做法是继承EnventObject类,随着事件的发生,相应的状态通常封装在此类中。在此处我们定义了一个时间戳,用于记录方法的开始执行时间。
/**
* 定义事件类型,通常的做法是继承 EnventObject,
* 随着事件的发生,相应的状态通常都封装在此类中。
* @author xiang.wei
* @date 2020/5/5 5:13 PM
*/
public class MethodMonitorEvent extends EventObject {
/**
* 时间戳,用于记录方法开始执行的时间
*/
protected long timestamp;
/**
* Constructs a prototypical Event.
*
* @param source The object on which the Event initially occurred.
* @throws IllegalArgumentException if source is null.
*/
public MethodMonitorEvent(Object source) {
super(source);
}
}
事件类型定义好之后,接下来,我们还需要定义一个监听器,用于监听事件的发生。我们可以在方法开始执行之前发布一个begin事件,在方法结束之后发布一个end事件。我们定义了事件监听接口,里面定义了处理begin事件的方法onMethodBegin
和处理end事件的方法onMethodEnd
。我们注意到这两个方法都只有一个参数,就是MethodMonitorEvent 参数。说明这个监听器类只负责监听对应的事件并进行处理。
/**
* 定义事件监听接口
* @author xiang.wei
* @date 2020/5/5 5:24 PM
*/
public interface MethodMonitorEventListener extends EventListener {
/**
* 处理方法执行之前发布的事件
* @param event
*/
void onMethodBegin(MethodMonitorEvent event);
/**
* 处理方法结束时发布的事件
* @param event
*/
void onMethodEnd(MethodMonitorEvent event);
}
在这个监听器接口的实现类里我们将会具体实现这两个方法的逻辑。方法也是很简单
public class MethodMonitorEventListenerImpl implements MethodMonitorEventListener {
@Override
public void onMethodBegin(MethodMonitorEvent event) {
//记录方法开始执行时的时间
event.timestamp = System.currentTimeMillis();
}
@Override
public void onMethodEnd(MethodMonitorEvent event) {
//计算耗时
long duration = System.currentTimeMillis() - event.timestamp;
System.out.println("总耗时:" + duration+" ms");
}
}
有了事件和监听器。我们还需要一个事件发布者,它本身作为一个事件源,在合适的时机,将相应的时间发布给对应的事件监听器。
public class MethodMonitorEventPublisher {
private List<MethodMonitorEventListener> eventListeners = new ArrayList<>();
public void methodMonitor() throws InterruptedException {
//定义事件
MethodMonitorEvent eventObject = new MethodMonitorEvent(this);
publishEvent("begin", eventObject);
//模拟方法执行:休眠5秒钟
TimeUnit.SECONDS.sleep(5);
publishEvent("end", eventObject);
}
//发布事件的逻辑
public void publishEvent(String status, MethodMonitorEvent event) {
//避免在事件处理期间,监听器被移除,这里为了安全做了一个复制操作
List<MethodMonitorEventListener> copyListeners = new ArrayList<>(eventListeners);
for (MethodMonitorEventListener listener : copyListeners) {
if ("begin".equals(status)) {
listener.onMethodBegin(event);
} else {
listener.onMethodEnd(event);
}
}
}
public static void main(String[] args) throws InterruptedException {
MethodMonitorEventPublisher publisher = new MethodMonitorEventPublisher();
//添加监听器
publisher.addEventListener(new MethodMonitorEventListenerImpl());
//发布事件
publisher.methodMonitor();
}
public void addEventListener(MethodMonitorEventListener listener) {
eventListeners.add(listener);
}
}
对于事件发布者(事件源)我们需要关注两点:
methodMonitor()
方法就是事件发布的源头,其在方法执行之前和结束之后两个时间点发布MethodMonitorEvent事件,每个时间点发布的事件都会传给相应的监听器进⾏处理。说完了Java提供的事件监听机制的两个基础类,以及如何实现一个自定义事件的demo。下面就请出本篇文章的主角Spring容器的时间监听机制。
Spring的ApplicationContext容器内部中的所有事件类型均继承自org.springframework.context.ApplicationEvent
容器中的所有监听器都实现了org.springframework.context.ApplicationListener
接口
并且以bean的形式注册到了容器中,一旦容器内发布ApplicationEvent
及其子类型的事件,注册到容器中的ApplicationListener
就会对这些事件进行处理。ApplicationEventMulticaster
类是事件管理者,管理监听器和发布事件,ApplicationContext
通过委托ApplicationEventMulticaster
来发布事件
ApplicationEventPublisher
是事件发布者,该接口封装了事件有关的公共方法,作为ApplicationContext
的超级街廓,也是委托 ApplicationEventMulticaster
完成事件发布。
在这里插入图片描述
ApplicationContextEvent
继承自ApplicationEvent
,而ApplicationEvent
继承自EventObject
,Spring提供了一些默认的实现,比如:
ContextStartedEvent
表示容器在启动时发布的事件类型,即调用start()
方法。
ContextRefreshedEvent
表示容器在初始化或者刷新的时候发布的事件类型,如调用refresh()
方法,此处的实例化是指所有的bean都已经被加载。后置处理器被激活。所有单例bean都已被实例化。所有的容器对象都已准备好可使用。
ContextStoppedEvent
表示容器在即将关闭时发布的事件类型,即调用了stop()
方法。
在这里插入图片描述
容器内部用ApplicationListener
作为事件监听器接口定义,它继承自EventListener
。ApplicationContext
容器在启动时,会自动识别并加载EventListener
类型的bean,一旦容器内有事件发布,将通知这些注册到容器的EventListener
。
其中ContextRefreshListener
监听器监听的是ContextRefreshedEvent
事件,而ContextCloserListener
监听器监听的是ContextClosedEvent
事件。
private class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
FrameworkServlet.this.onApplicationEvent(event);
}
}
而ApplicationContext
接口继承了ApplicationEventPublisher
接口,该接口提供了void publishEvent(ApplicationEvent event)
方法的定义,不难看出,ApplicationContext容器担当的就是事件发布者的角色。需要说明的是Spring事件默认是同步的,即调用publishEvent
方法发布事件后,它会处于阻塞状态,直到onApplicationEvent
接受到事件并处理返回之后才继续执行下去,这种单线程同步的好处是可以进行任务管理。
本文首先介绍了Java中事件监听机制的基本概念,并且以一个记录方法耗时的demo说明了如何自定义事件类型。接着就是介绍了Spring容器的事件监听机制。