溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Tomcat和Spring事件機制的示例分析

發(fā)布時間:2021-08-21 11:17:34 來源:億速云 閱讀:142 作者:小新 欄目:編程語言

這篇文章主要介紹了Tomcat和Spring事件機制的示例分析,具有一定借鑒價值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。

引言

最近在看tomcat源碼,源碼中出現(xiàn)了大量事件消息,可以說整個tomcat的啟動流程都可以通過事件派發(fā)機制串起來,研究透了tomcat的各種事件消息,基本上對tomcat的啟動流程也就有了一個整體的認(rèn)識。在這一基礎(chǔ)上,聯(lián)想到之前在看spring源碼過程中也存在不少事件相關(guān)知識,于是想對這兩個框架中的事件派發(fā)機制做一個簡單的總結(jié),加深理解。

事件機制原理其實比較簡單,抽象來看的話,設(shè)計模式中的觀察者模式可以說是最經(jīng)典的事件驅(qū)動機制的體現(xiàn)了,觀察者和被觀察者就體現(xiàn)了事件監(jiān)聽和事件派發(fā)的角色。還有各種MQ,其實也是事件機制的一種體現(xiàn)。

理解tomcat和spring中的事件機制之前,讓我們先從最基本的jdk中提供的事件機制開始說起。

JDK中的事件機制

JDK中對事件機制的各個角色提供了完善的抽象,主要包括3個角色:

EventObject(事件關(guān)注內(nèi)容):事件發(fā)布時需要關(guān)注的內(nèi)容。jdk中提供了EventObject接口。

EventListener(事件監(jiān)聽者):事件監(jiān)聽對象,也就是對EventObject感興趣的對象。jdk中提供了EventListener接口。

EventSource(事件源):發(fā)布事件的對象,可以在該對象中組冊EventListener,然后在特定的條件下發(fā)布EventObject給已經(jīng)注冊的EventListener。

事件的注冊與發(fā)布,需要這三個對象協(xié)同工作,可以通過下面的例子來說明各個對象的作用:

首先是事件關(guān)注內(nèi)容對象MyEventObject,實現(xiàn)了EventObject接口。eventName參數(shù)為具體的事件關(guān)注內(nèi)容

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;
}

其次是事件監(jiān)聽接口MyEventListener,繼承了EventListener,定義了一個myEvent接口用來發(fā)布事件,任何感興趣的監(jiān)聽對象都可以實現(xiàn)該接口來監(jiān)聽。

對MyEventObject感興趣的監(jiān)聽者MyEventListenerImpl,實現(xiàn)了MyEventListener接口,當(dāng)事件發(fā)布時會觸發(fā)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());
 }
}

最后是事件發(fā)布源對象MyEventSource,它可以注冊多個事件監(jiān)聽對象,任何實現(xiàn)了MyEventListener接口的監(jiān)聽對象都可以注冊,內(nèi)部通過一個Set來存儲感興趣的監(jiān)聽對象,并在合適的時機會發(fā)布消息并通知所有監(jiān)聽對象。

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
  //發(fā)布push event消息
  notifyListener(new MyEventObject(this, "push event"));
 }
 
 private void notifyListener(MyEventObject eventObject){
  
  for (MyEventListener myEventListener : myEventListeners) {
   myEventListener.myEvent(eventObject);
  }
 }
}

之后可以通過一個啟動類來注冊并觸發(fā)事件:

public static void main(String[] args) {
 
 MyEventSource myEventSource = new MyEventSource();
 
 MyEventListenerImpl myEventListenerImpl = new MyEventListenerImpl();
 
 myEventSource.addListener(myEventListenerImpl);
 
 myEventSource.pushEvent();
}

MyEventObject定義了感興趣的內(nèi)容,MyEventListenerImpl是對MyEventObject感興趣的監(jiān)聽者,MyEventSource會發(fā)布MyEventObject給所有組冊的監(jiān)聽者,最后通過一個main來啟動整個流程。

明白了jdk中對事件機制的定義,再來看看tomcat和spring中的事件機制。

Tomcat的事件機制

tomcat的事件機制也離不開EventObject、EventListener以及EventSource三個對象,只不過在此基礎(chǔ)上提供了更加抽象和便捷的操作。這里我挑選tomcat的生命周期接口對象Lifecycle來講解整個事件發(fā)布流程:

首先還是EventObject對象LifecycleEvent,這里只列出了核心代碼。它的主要參數(shù)是Lifecycle,Lifecycle中定義了tomcat各個階段的名稱:before_init、after_init、start等等,是事件監(jiān)聽者感興趣的對象。

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";

 //......
}

事件監(jiān)聽接口LifecycleListener,定義了lifecycleEvent方法用來傳遞監(jiān)聽者感興趣的LifecycleEvent對象,監(jiān)聽者使用LifecycleEvent參數(shù)用來在tomcat的各個階段處理進行相應(yīng)處理。這些感興趣的對象包括下面這些類:

Tomcat和Spring事件機制的示例分析

這里使用ContextConfig類為例,可以看到它實現(xiàn)了LifecycleListener接口。這個類在解析server.xml的時候用來監(jiān)聽StandardContext的各個階段的事件,并做出相應(yīng)處理:

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是我們需要了解的主要對象,它是監(jiān)聽對象的一個管理類,原理其實和上面的例子差不多,對應(yīng)了MyEventSource類的部分功能,方便EventSource類來管理監(jiān)聽對象。它把對監(jiān)聽對象的添加移除以及發(fā)布事件幾個操作進行了統(tǒng)一管理,避免EventSource類中出現(xiàn)太多管理監(jiān)聽對象的邏輯。

public final class LifecycleSupport {

 //......

 //監(jiān)聽對象集合
 private LifecycleListener listeners[] = new LifecycleListener[0];
 
 private final Object listenersLock = new Object(); // Lock object for changes to listeners

 //添加監(jiān)聽對象
 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;
  }

 }

 //發(fā)布監(jiān)聽對象
 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);

 }

 //移除監(jiān)聽對象
 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就簡單多了,只需要調(diào)用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);
 }
 //......
}

在需要發(fā)布事件時調(diào)用fireLifecycleEvent方法就可以發(fā)布事件:

fireLifecycleEvent(Lifecycle.CONFIGURE_STOP_EVENT, null);

tomcat事件機制就是在之前的例子上抽出了一個LifecycleSupport類來方便管理監(jiān)聽對象的各種操作,這是一個可以借鑒的地方,其他差別并不大。再來看看spring中對事件機制的處理。

Spring的事件機制

spring中的事件機制原理也是一樣的,只是相對來說實現(xiàn)上稍微復(fù)雜一點。還是通過相同的角度來看這個問題。

首先是EventObject,spring里面的主要實現(xiàn)是ApplicationEvent:

Tomcat和Spring事件機制的示例分析

這里通過ContextStartedEvent類來查看EventObject,它關(guān)注的對象是ApplicationContext,是spring容器在啟動時觸發(fā)的事件對象:

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);
 }
}

事件監(jiān)聽接口ApplicationListener,定義了onApplicationEvent方法用來傳遞監(jiān)聽者感興趣的ApplicationEvent對象,監(jiān)聽者使用ApplicationEvent參數(shù)用來在Context的各個階段處理進行相應(yīng)處理。

如果我們需要在容器啟動后進行相應(yīng)處理,那么我們可以在業(yè)務(wù)類中實現(xiàn)ApplicationListener接口,在事件發(fā)生時就會發(fā)起通知:

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框架中是怎么發(fā)布這些事件的呢?是不是也有一個類似tomcat中LifecycleSupport一樣的類呢?通過查看源碼可以發(fā)現(xiàn)發(fā)現(xiàn),ApplicationContext容器在初始化階段會調(diào)用refresh()方法,這其中又調(diào)用了
finishRefresh()方法,這其中調(diào)用了publishEvent(new ContextRefreshedEvent(this))方法,發(fā)布了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方法通過調(diào)用一個默認(rèn)的多播器SimpleApplicationEventMulticaster的multicastEvent方法來發(fā)布各種事件:

SimpleApplicationEventMulticaster
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
 ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));

 //通過getApplicationListeners獲取了所有監(jiān)聽器,然后通過invokeListener方法循環(huán)發(fā)布事件
 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容器中發(fā)布ApplicationListener所關(guān)注的對象是通過SimpleApplicationEventMulticaster這個類來管理的,和tomcat中LifecycleSupport的功能類似,只是在實現(xiàn)上有略微差別。

最后提一句,在spring中你也可以自己發(fā)布各種事件,調(diào)用ApplicationContext的publishEvent方法即可。

applicationContext.publishEvent(new ApplicationEvent(new String("事件發(fā)布")) { });

感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享的“Tomcat和Spring事件機制的示例分析”這篇文章對大家有幫助,同時也希望大家多多支持億速云,關(guān)注億速云行業(yè)資訊頻道,更多相關(guān)知識等著你來學(xué)習(xí)!

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI