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的事件机制有一个更深刻的认识,在实现自己的事件机制时有可以借鉴以及改进的地方。

好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • Spring的事件监听机制示例详解

    前言 最近公司在重构广告系统,其中核心的打包功由广告系统调用,即对apk打包的调用和打包完成之后的回调,需要提供相应的接口给广告系统.因此,为了将apk打包的核心流程和对接广告系统的业务解耦,利用了spring的事件监听特性来满足需求.以下说明spring的事件机制的相关内容. 首先spring事件分为事件发布者(EventPublisher).事件监听者(EventListener),还包括一个事件广播者(这个是spring实现相关,这一节不讨论).使用spring事件机制,需要自定义事件发布

  • Spring 事务事件监控及实现原理解析

    前面我们讲到了Spring在进行事务逻辑织入的时候,无论是事务开始,提交或者回滚,都会触发相应的事务事件.本文首先会使用实例进行讲解Spring事务事件是如何使用的,然后会讲解这种使用方式的实现原理. 1. 示例 对于事务事件,Spring提供了一个注解@TransactionEventListener,将这个注解标注在某个方法上,那么就将这个方法声明为了一个事务事件处理器,而具体的事件类型则是由TransactionalEventListener.phase属性进行定义的.如下是Transac

  • 使用Spring事件机制实现异步的方法

    当把一个事件发布到Spring提供的ApplicationContext中,被监听器侦测到,就会执行对应的处理方法. 事件本身 事件是一个自定义的类,需要继承Spring提供的ApplicationEvent. @Data public class MyEvent extends ApplicationEvent { private String msg; public MyEvent(Object source, String msg) { super(source); this.msg =

  • Spring事务事件监控的实现

    前面我们讲到了Spring在进行事务逻辑织入的时候,无论是事务开始,提交或者回滚,都会触发相应的事务事件.本文首先会使用实例进行讲解Spring事务事件是如何使用的,然后会讲解这种使用方式的实现原理. 1.示例 对于事务事件,Spring提供了一个注解@TransactionEventListener,将这个注解标注在某个方法上,那么就将这个方法声明为了一个事务事件处理器,而具体的事件类型则是由TransactionalEventListener.phase属性进行定义的.如下是Transact

  • 基于spring如何实现事件驱动实例代码

    干货点 通过阅读该篇博客,你可以了解了解java的反射机制.可以了解如何基于spring生命周期使用自定义注解解决日常研发问题.具体源码可以点击链接. 问题描述 在日常研发中,经常会遇见业务A的某个action被触发后,同时触发业务B的action的行为,这种单对单的形式可以直接在业务A的action执行结束后直接调用业务B的action,那么如果是单对多的情况呢? 方案解决 这里提供一种在日常研发中经常使用到的机制,基于spring实现的事件驱动,即在业务A的action执行完,抛出一个事件,

  • Tomcat和Spring中的事件机制深入讲解

    引言 最近在看tomcat源码,源码中出现了大量事件消息,可以说整个tomcat的启动流程都可以通过事件派发机制串起来,研究透了tomcat的各种事件消息,基本上对tomcat的启动流程也就有了一个整体的认识.在这一基础上,联想到之前在看spring源码过程中也存在不少事件相关知识,于是想对这两个框架中的事件派发机制做一个简单的总结,加深理解. 事件机制原理其实比较简单,抽象来看的话,设计模式中的观察者模式可以说是最经典的事件驱动机制的体现了,观察者和被观察者就体现了事件监听和事件派发的角色.还

  • 详解JAVA Spring 中的事件机制

    说到事件机制,可能脑海中最先浮现的就是日常使用的各种 listener,listener去监听事件源,如果被监听的事件有变化就会通知listener,从而针对变化做相应的动作.这些listener是怎么实现的呢?说listener之前,我们先从设计模式开始讲起. 观察者模式 观察者模式一般包含以下几个对象: Subject:被观察的对象.它提供一系列方法来增加和删除观察者对象,同时它定义了通知方法notify().目标类可以是接口,也可以是抽象类或具体类. ConcreteSubject:具体的

  • 深入浅出 jQuery中的事件机制

    首先呢,我们先来看一下,jQuery中的ready事件,它的作用有些类似于window中的onload事件,只不过二者的区别在于,onload事件是要等到网页中的dom节点加载完成,并且css进行样式渲染后,JavaScript中进行了预加载后再执行,而ready事件呢,是在网页中的标签被解析为DOM节点是就执行其中的函数,直白点说,ready执行的比onload早,那么我们在用jQuery的时候,需要做一个ready的执行定义: //ready的作用,是为具体加载的实现. $(document

  • 基于spring中的aop简单实例讲解

    aop,即面向切面编程,面向切面编程的目标就是分离关注点,比如:一个骑士只需要关注守护安全,或者远征,而骑士辉煌一生的事迹由谁来记录和歌颂呢,当然不会是自己了,这个完全可以由诗人去歌颂,比如当骑士出征的时候诗人可以去欢送,当骑士英勇牺牲的时候,诗人可以写诗歌颂骑士的一生.那么骑士只需要关注怎么打仗就好了.而诗人也只需要关注写诗歌颂和欢送就好了,那么这样就把功能分离了.所以可以把诗人当成一个切面,当骑士出征的前后诗人分别负责欢送和写诗歌颂(记录).而且,这个切面可以对多个骑士或者明人使用,并不只局

  • 深入解析JavaScript框架Backbone.js中的事件机制

    事件模型及其原理 Backbone.Events就是事件实现的核心,它可以让对象拥有事件能力 var Events = Backbone.Events = { .. } 对象通过listenTo侦听其他对象,通过trigger触发事件.可以脱离Backbone的MVC,在自定义的对象上使用事件 var model = _.extend({},Backbone.Events); var view = _.extend({},Backbone.Events); view.listenTo(model

  • 详解Node.js中的事件机制

    前言 在前端编程中,事件的应用十分广泛,DOM上的各种事件.在Ajax大规模应用之后,异步请求更得到广泛的认同,而Ajax亦是基于事件机制的. 通常js给我们的第一印象就是运行在客户端浏览器上面的脚本,通过node.js我们可以在服务端运行javascript. node.js是基于单线程无阻塞异步式的I/O,异步式的I/O指的是当遇到I/O操作的时候,线程不阻塞而是进行下面的操作,那么I/O操作完成之后,线程时如何知道该操作完成的呢? 当操作完成耗时的I/O操作之后,会以事件的形式通知I/O操

  • spring-boot中的SPI机制实例讲解

    一.从java类加载机制说起 java中的类加载器负载加载来自文件系统.网络或者其他来源的类文件.jvm的类加载器默认使用的是双亲委派模式.三种默认的类加载器Bootstrap ClassLoader.Extension ClassLoader和System ClassLoader(Application ClassLoader)每一个中类加载器都确定了从哪一些位置加载文件.于此同时我们也可以通过继承java.lang.classloader实现自己的类加载器. Bootstrap ClassL

  • Kotlin中的反射机制深入讲解

    前言 Java中的反射机制,使得我们可以在运行期获取Java类的字节码文件中的构造函数,成员变量,成员函数等信息.这一特性使得反射机制被常常用在框架中,想要比较系统的了解Kotlin中的反射,先从Java的反射说起. Java中的反射 通常我们写好的.java源码文件,经过javac的编译,最终生成了.class字节码文件.这些字节码文件是与平台无关的,使用时通过Classloader去加载这些.class字节码文件,从而让程序按照我们编写好的业务逻辑运行.Java的反射主要是从这些.class

  • Spring中@Autowire注入的深入讲解

    一直在思考spring的@Autowire注入属性时到底是按类型注入还是按名称注入,今天写了一个测试来证明一下. 定义接口TestService public interface TestService { void test(); } 定义接口实现:TestServiceImpl1和TestServiceImpl2 @Service public class TestServiceImpl1 implements TestService { public void test() { Syste

  • 浅析JavaScript中的事件机制

     事件是什么 ? JavaScript与HTML交互是通过在用户或浏览器操纵页面上发生的事件进行处理. 当页面加载,这是一个事件.当用户点击一个按钮,这一下,也就是一个事件.事件的另一个例子是类似按下任意键,关闭窗口,调整窗口等. 开发者可以使用这些事件执行JavaScript编码响应,这引起按钮以关闭视窗,消息,以便显示给用户,要验证的数据,以及几乎任何其它类型的响应可以发生的. 事件是文档对象模型(DOM)第3级,每一个HTML元素的一部分有一套可以触发JavaScript代码事件. 例子:

随机推荐