Tomcat与Spring中的事件机制详解
最近在看tomcat源码,源码中出现了大量事件消息,可以说整个tomcat的启动流程都可以通过事件派发机制串起来,研究透了tomcat的各种事件消息,基本上对tomcat的启动流程也就有了一个整体的认识。在这一基础上,联想到之前在看spring源码过程中也存在不少事件相关知识,于是想对这两个框架中的事件派发机制做一个简单的总结,加深理解。
事件机制原理其实比较简单,抽象来看的话,设计模式中的观察者模式可以说是最经典的事件驱动机制的体现了,观察者和被观察者就体现了事件监听和事件派发的角色。还有各种MQ,其实也是事件机制的一种体现。
理解tomcat和spring中的事件机制之前,让我们先从最基本的jdk中提供的事件机制开始说起。
JDK中的事件机制
JDK中对事件机制的各个角色提供了完善的抽象,主要包括3个角色:
EventObject(事件关注内容):事件发布时需要关注的内容。jdk中提供了EventObject接口。
EventListener(事件监听者):事件监听对象,也就是对EventObject感兴趣的对象。jdk中提供了EventListener接口。
EventSource(事件源):发布事件的对象,可以在该对象中组册EventListener,然后在特定的条件下发布EventObject给已经注册的EventListener。
事件的注册与发布,需要这三个对象协同工作,可以通过下面的例子来说明各个对象的作用:
首先是事件关注内容对象MyEventObject,实现了EventObject接口。eventName参数为具体的事件关注内容
public class MyEventObject extends EventObject {
private String eventName ;
public MyEventObject (Object source, String eventName) {
super(source);
this.setEventName(eventName);
}
public String getEventName() {
return eventName;
}
public void setEventName(String eventName) {
this.eventName = eventName;
}
private static final long serialVersionUID = 8374250957018011175L;
}
复制代码
其次是事件监听接口MyEventListener,继承了EventListener,定义了一个myEvent接口用来发布事件,任何感兴趣的监听对象都可以实现该接口来监听。
对MyEventObject感兴趣的监听者MyEventListenerImpl,实现了MyEventListener接口,当事件发布时会触发myEvent事件并收到MyEventObject对象。
public interface MyEventListener extends EventListener {
public void myEvent(MyEventObject eventObject);
}
public class MyEventListenerImpl implements MyEventListener {
@Override
public void myEvent(MyEventObject eventObject) {
System.out.println("MyEventListenerImpl --- " + eventObject.getEventName());
}
}
复制代码
最后是事件发布源对象MyEventSource,它可以注册多个事件监听对象,任何实现了MyEventListener接口的监听对象都可以注册,内部通过一个Set来存储感兴趣的监听对象,并在合适的时机会发布消息并通知所有监听对象。
public class MyEventSource {
private Set<MyEventListener> myEventListeners = new HashSet<>();
public void addListener(MyEventListener listener){
this.myEventListeners.add(listener);
}
public void removeListener(MyEventListener listener){
this.myEventListeners.remove(listener);
}
public void pushEvent(){
//dosomething
//发布push event消息
notifyListener(new MyEventObject(this, "push event"));
}
private void notifyListener(MyEventObject eventObject){
for (MyEventListener myEventListener : myEventListeners) {
myEventListener.myEvent(eventObject);
}
}
}
复制代码
之后可以通过一个启动类来注册并触发事件:
public static void main(String[] args) {
MyEventSource myEventSource = new MyEventSource();
MyEventListenerImpl myEventListenerImpl = new MyEventListenerImpl();
myEventSource.addListener(myEventListenerImpl);
myEventSource.pushEvent();
}
复制代码
MyEventObject定义了感兴趣的内容,MyEventListenerImpl是对MyEventObject感兴趣的监听者,MyEventSource会发布MyEventObject给所有组册的监听者,最后通过一个main来启动整个流程。
明白了jdk中对事件机制的定义,再来看看tomcat和spring中的事件机制。
Tomcat的事件机制
tomcat的事件机制也离不开EventObject、EventListener以及EventSource三个对象,只不过在此基础上提供了更加抽象和便捷的操作。这里我挑选tomcat的生命周期接口对象Lifecycle来讲解整个事件发布流程:
首先还是EventObject对象LifecycleEvent,这里只列出了核心代码。它的主要参数是Lifecycle,Lifecycle中定义了tomcat各个阶段的名称:before_init、after_init、start等等,是事件监听者感兴趣的对象。
public final class LifecycleEvent extends EventObject {
//......
public LifecycleEvent(Lifecycle lifecycle, String type, Object data) {
super(lifecycle);
this.type = type;
this.data = data;
}
//......
}
public interface Lifecycle {
/**
* The LifecycleEvent type for the "component after init" event.
*/
public static final String BEFORE_INIT_EVENT = "before_init";
/**
* The LifecycleEvent type for the "component after init" event.
*/
public static final String AFTER_INIT_EVENT = "after_init";
/**
* The LifecycleEvent type for the "component start" event.
*/
public static final String START_EVENT = "start";
//......
}
复制代码
事件监听接口LifecycleListener,定义了lifecycleEvent方法用来传递监听者感兴趣的LifecycleEvent对象,监听者使用LifecycleEvent参数用来在tomcat的各个阶段处理进行相应处理。这些感兴趣的对象包括下面这些类:
这里使用ContextConfig类为例,可以看到它实现了LifecycleListener接口。这个类在解析server.xml的时候用来监听StandardContext的各个阶段的事件,并做出相应处理:
public interface LifecycleListener {
public void lifecycleEvent(LifecycleEvent event);
}
public class ContextConfig implements LifecycleListener {
//......
@Override
public void lifecycleEvent(LifecycleEvent event) {
// Identify the context we are associated with
try {
context = (Context) event.getLifecycle();
} catch (ClassCastException e) {
log.error(sm.getString("contextConfig.cce", event.getLifecycle()), e);
return;
}
// Process the event that has occurred
if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {
configureStart();
} else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) {
beforeStart();
} else if (event.getType().equals(Lifecycle.AFTER_START_EVENT)) {
// Restore docBase for management tools
if (originalDocBase != null) {
context.setDocBase(originalDocBase);
}
} else if (event.getType().equals(Lifecycle.CONFIGURE_STOP_EVENT)) {
configureStop();
} else if (event.getType().equals(Lifecycle.AFTER_INIT_EVENT)) {
init();
} else if (event.getType().equals(Lifecycle.AFTER_DESTROY_EVENT)) {
destroy();
}
}
//......
}
复制代码
LifecycleSupport是我们需要了解的主要对象,它是监听对象的一个管理类,原理其实和上面的例子差不多,对应了MyEventSource类的部分功能,方便EventSource类来管理监听对象。它把对监听对象的添加移除以及发布事件几个操作进行了统一管理,避免EventSource类中出现太多管理监听对象的逻辑。
public final class LifecycleSupport {
//......
//监听对象集合
private LifecycleListener listeners[] = new LifecycleListener[0];
private final Object listenersLock = new Object(); // Lock object for changes to listeners
//添加监听对象
public void addLifecycleListener(LifecycleListener listener) {
synchronized (listenersLock) {
LifecycleListener results[] =
new LifecycleListener[listeners.length + 1];
for (int i = 0; i < listeners.length; i++)
results[i] = listeners[i];
results[listeners.length] = listener;
listeners = results;
}
}
//发布监听对象
public void fireLifecycleEvent(String type, Object data) {
LifecycleEvent event = new LifecycleEvent(lifecycle, type, data);
LifecycleListener interested[] = listeners;
for (int i = 0; i < interested.length; i++)
interested[i].lifecycleEvent(event);
}
//移除监听对象
public void removeLifecycleListener(LifecycleListener listener) {
synchronized (listenersLock) {
int n = -1;
for (int i = 0; i < listeners.length; i++) {
if (listeners[i] == listener) {
n = i;
break;
}
}
if (n < 0)
return;
LifecycleListener results[] =
new LifecycleListener[listeners.length - 1];
int j = 0;
for (int i = 0; i < listeners.length; i++) {
if (i != n)
results[j++] = listeners[i];
}
listeners = results;
}
}
}
复制代码
使用了LifecycleSupport之后,操作LifecycleListener就简单多了,只需要调用LifecycleSupport的各个方法就可以了:
public abstract class LifecycleBase implements Lifecycle{
//......
private LifecycleSupport lifecycle = new LifecycleSupport(this);
@Override
public void addLifecycleListener(LifecycleListener listener) {
lifecycle.addLifecycleListener(listener);
}
@Override
public void removeLifecycleListener(LifecycleListener listener) {
lifecycle.removeLifecycleListener(listener);
}
protected void fireLifecycleEvent(String type, Object data) {
lifecycle.fireLifecycleEvent(type, data);
}
//......
}
复制代码
在需要发布事件时调用fireLifecycleEvent方法就可以发布事件:
fireLifecycleEvent(Lifecycle.CONFIGURE_STOP_EVENT, null);
复制代码
tomcat事件机制就是在之前的例子上抽出了一个LifecycleSupport类来方便管理监听对象的各种操作,这是一个可以借鉴的地方,其他差别并不大。再来看看spring中对事件机制的处理。
Spring的事件机制
spring中的事件机制原理也是一样的,只是相对来说实现上稍微复杂一点。还是通过相同的角度来看这个问题。
首先是EventObject,spring里面的主要实现是ApplicationEvent:
这里通过ContextStartedEvent类来查看EventObject,它关注的对象是ApplicationContext,是spring容器在启动时触发的事件对象:
public abstract class ApplicationEvent extends EventObject {
//......
public ApplicationEvent(Object source) {
super(source);
this.timestamp = System.currentTimeMillis();
}
//......
}
public abstract class ApplicationContextEvent extends ApplicationEvent {
public ApplicationContextEvent(ApplicationContext source) {
super(source);
}
public final ApplicationContext getApplicationContext() {
return (ApplicationContext)this.getSource();
}
}
public class ContextStartedEvent extends ApplicationContextEvent {
public ContextStartedEvent(ApplicationContext source) {
super(source);
}
}
复制代码
事件监听接口ApplicationListener,定义了onApplicationEvent方法用来传递监听者感兴趣的ApplicationEvent对象,监听者使用ApplicationEvent参数用来在Context的各个阶段处理进行相应处理。
如果我们需要在容器启动后进行相应处理,那么我们可以在业务类中实现ApplicationListener接口,在事件发生时就会发起通知:
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
void onApplicationEvent(E event);
}
public class MyApplicationListener implements ApplicationListener<ApplicationEvent> {
@Override
public void onApplicationEvent(ApplicationEvent applicationEvent) {
if (applicationEvent instanceof ContextRefreshedEvent){
System.out.println("context refresh!");
}
}
}
复制代码
那么在spring框架中是怎么发布这些事件的呢?是不是也有一个类似tomcat中LifecycleSupport一样的类呢?通过查看源码可以发现发现,ApplicationContext容器在初始化阶段会调用refresh()方法,这其中又调用了 finishRefresh()方法,这其中调用了publishEvent(new ContextRefreshedEvent(this))方法,发布了ContextRefreshedEvent这一对象。
protected void finishRefresh() {
//......
// Publish the final event.
publishEvent(new ContextRefreshedEvent(this));
}
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
//......
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
//......
}
publishEvent方法通过调用一个默认的多播器SimpleApplicationEventMulticaster的multicastEvent方法来发布各种事件:
SimpleApplicationEventMulticaster
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
//通过getApplicationListeners获取了所有监听器,然后通过invokeListener方法循环发布事件
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
Executor executor = getTaskExecutor();
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
//......
doInvokeListener(listener, event);
}
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
//......
listener.onApplicationEvent(event);
}
复制代码
也就是说在spring容器中发布ApplicationListener所关注的对象是通过SimpleApplicationEventMulticaster这个类来管理的,和tomcat中LifecycleSupport的功能类似,只是在实现上有略微差别。
最后提一句,在spring中你也可以自己发布各种事件,调用ApplicationContext的publishEvent方法即可。
applicationContext.publishEvent(new ApplicationEvent(new String("事件发布")) { });
复制代码
总结
这篇文章对Java的事件机制在tomcat以及spring框架中的实现做了一个简单总结和对比,你需要知道以下几点:
- JDK中定义了EventObject和EventListener两个接口,奠定了事件机制的基础。
- Tomcat额外提供了一个support类来对监听器的添加删除以及发布进行管理。
- Spring容器内部通过SimpleApplicationEventMulticaster来发布各个事件,用户可以通过实现ApplicationListener接口来监听自己感兴趣的容器事件。
希望你通过这篇文章的学习可以对Java的事件机制有一个更深刻的认识,在实现自己的事件机制时有可以借鉴以及改进的地方。
作者:knock_小新
链接:https://juejin.im/post/5c17b9086fb9a049a81f3c81
Tomcat与Spring中的事件机制详解的更多相关文章
- Spring 中的事件机制
说到事件机制,可能脑海中最先浮现的就是日常使用的各种 listener,listener去监听事件源,如果被监听的事件有变化就会通知listener,从而针对变化做相应的动作.这些listener是怎 ...
- Spring的事件机制详解
同步事件和异步事件 同步事件:在一个线程里,按顺序执行业务,做完一件事再去做下一件事. 异步事件:在一个线程里,做一个事的同事,可以另起一个新的线程执行另一件事,这样两件事可以同时执行. 用一个例子来 ...
- Spring事件机制详解
一.前言 说来惭愧,对应Spring事件机制之前只知道实现 ApplicationListener 接口,就可以基于Spring自带的事件做一些事情(如ContextRefreshedEvent),但 ...
- spring中bean的配置详解--定义parent
在工作中碰到了好多的配置文件,具体来说是spring 中bean配置的parent的配置,搞的我一头雾水,仔细看一下spring中有关bean的配置,剖析一下,具体什么含义! 一.Spring IoC ...
- 【移动端兼容问题研究】javascript事件机制详解(涉及移动兼容)
前言 这篇博客有点长,如果你是高手请您读一读,能对其中的一些误点提出来,以免我误人子弟,并且帮助我提高 如果你是javascript菜鸟,建议您好好读一读,真的理解下来会有不一样的收获 在下才疏学浅, ...
- JAVA中的GC机制详解
优秀Java程序员必须了解的GC工作原理 一个优秀的Java程序员必须了解GC的工作原理.如何优化GC的性能.如何与GC进行有限的交互,因为有一些应用程序对性能要求较高,例如嵌入式系统.实时系统等,只 ...
- Spring中的事务管理详解
在这里主要介绍Spring对事务管理的一些理论知识,实战方面参考上一篇博文: http://www.cnblogs.com/longshiyVip/p/5061547.html 1. 事务简介: 事务 ...
- Spring中注解的使用详解
一:@Rsource注解的使用规则 1.1.案例演示 Spring的主配置文件:applicationContext.xml(因为我这里将会讲到很多模块,所以我用一个主配置文件去加载各个模块的配置文件 ...
- spring中的idref标签详解
spring中的idref元素 idref元素是一个简单的对容器中存在的另外一个bean的检错途径(通过id); <idref bean="someBeanId"/> ...
随机推荐
- js 函数讲解
函数 来自<JavaScript 标准参考教程(alpha)>,by 阮一峰 目录 概述 函数的声明 函数的重复声明 圆括号运算符,return 语句和递归 第一等公民 函数名的提升 不能 ...
- [转帖] CentOS7 与 CentOS6的对比
来源网站: http://blog.51cto.com/fengery/1901349 centos6.x_centos7.x差异改进明细 感谢原作者 centos官方帮助文档:https://wik ...
- git bash使用(markdown版)
前言 我是通过这个来学习的.个人愚笨,琢磨了半天,终于搞通了,醉了醉了,以前一直使用svn,用git确实有点水土不服.本文以如何使用git为主来展开,不涉及太多理论. git是分布式的版本管理.什么叫 ...
- HttpURLConnection、HttpClient和Session
原文地址:http://www.cnblogs.com/kross/p/3615695.html 一直没弄懂Session,cookies什么的登陆验证到底是怎么回事,昨天分别用HttpURLConn ...
- nodejs 新特性
一般时间没看nodejs了,又出了一些新特性了. 异步钩子 async_hooks 先看相关的文章吧 https://zhuanlan.zhihu.com/p/27394440 性能 ...
- mysql索引的优化
MySQL索引的优化 上面都在说使用索引的好处,但过多的使用索引将会造成滥用.因此索引也会有它的缺点:虽然索引大大提高了查询速度,同时却会降低更新表的速度,如对表进行INSERT.UPDATE和DEL ...
- c++11 委托构造
c++11 委托构造 #define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <string> #includ ...
- .NET 复制对象会影响到复制源对象
IList<string> list=new List<string>(); list.add("a"); list.add("b"); ...
- Reactor模式,或者叫反应器模式 - 为什么用多路io复用提供吞吐量
Reactor这个词译成汉语还真没有什么合适的,很多地方叫反应器模式,但更多好像就直接叫reactor模式了,其实我觉着叫应答者模式更好理解一些.通过了解,这个模式更像一个侍卫,一直在等待你的召唤,或 ...
- C++继承与组合的区别
C++程序开发中,设计孤立的类比较容易,设计相互关联的类却比较难,这其中会涉及到两个概念,一个是继承(Inheritance),一个是组合(Composition).因为二者有一定的相似性,往往令程序 ...