Tomcat和Spring中的事件機(jī)制深入講解
引言
最近在看tomcat源碼,源碼中出現(xiàn)了大量事件消息,可以說整個tomcat的啟動流程都可以通過事件派發(fā)機(jī)制串起來,研究透了tomcat的各種事件消息,基本上對tomcat的啟動流程也就有了一個整體的認(rèn)識。在這一基礎(chǔ)上,聯(lián)想到之前在看spring源碼過程中也存在不少事件相關(guān)知識,于是想對這兩個框架中的事件派發(fā)機(jī)制做一個簡單的總結(jié),加深理解。
事件機(jī)制原理其實比較簡單,抽象來看的話,設(shè)計模式中的觀察者模式可以說是最經(jīng)典的事件驅(qū)動機(jī)制的體現(xiàn)了,觀察者和被觀察者就體現(xiàn)了事件監(jiān)聽和事件派發(fā)的角色。還有各種MQ,其實也是事件機(jī)制的一種體現(xiàn)。
理解tomcat和spring中的事件機(jī)制之前,讓我們先從最基本的jdk中提供的事件機(jī)制開始說起。
JDK中的事件機(jī)制
JDK中對事件機(jī)制的各個角色提供了完善的抽象,主要包括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)聽者M(jìn)yEventListenerImpl,實現(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)聽對象,并在合適的時機(jī)會發(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中對事件機(jī)制的定義,再來看看tomcat和spring中的事件機(jī)制。
Tomcat的事件機(jī)制
tomcat的事件機(jī)制也離不開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的各個階段處理進(jìn)行相應(yīng)處理。這些感興趣的對象包括下面這些類:
這里使用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ā)布事件幾個操作進(jìn)行了統(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事件機(jī)制就是在之前的例子上抽出了一個LifecycleSupport類來方便管理監(jiān)聽對象的各種操作,這是一個可以借鑒的地方,其他差別并不大。再來看看spring中對事件機(jī)制的處理。
Spring的事件機(jī)制
spring中的事件機(jī)制原理也是一樣的,只是相對來說實現(xiàn)上稍微復(fù)雜一點。還是通過相同的角度來看這個問題。
首先是EventObject,spring里面的主要實現(xiàn)是ApplicationEvent:
這里通過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的各個階段處理進(jìn)行相應(yīng)處理。
如果我們需要在容器啟動后進(jìn)行相應(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ā)布")) { });
總結(jié)
這篇文章對Java的事件機(jī)制在tomcat以及spring框架中的實現(xiàn)做了一個簡單總結(jié)和對比,你需要知道以下幾點:
- JDK中定義了EventObject和EventListener兩個接口,奠定了事件機(jī)制的基礎(chǔ)。
- Tomcat額外提供了一個support類來對監(jiān)聽器的添加刪除以及發(fā)布進(jìn)行管理。
- Spring容器內(nèi)部通過SimpleApplicationEventMulticaster來發(fā)布各個事件,用戶可以通過實現(xiàn)ApplicationListener接口來監(jiān)聽自己感興趣的容器事件。
希望你通過這篇文章的學(xué)習(xí)可以對Java的事件機(jī)制有一個更深刻的認(rèn)識,在實現(xiàn)自己的事件機(jī)制時有可以借鑒以及改進(jìn)的地方。
好了,以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。
相關(guān)文章
JavaSE程序邏輯控制實現(xiàn)詳細(xì)圖文教程
JavaSE是為了開發(fā)桌面應(yīng)用程序和控制臺應(yīng)用程序而設(shè)計的,使用JavaSE可以編寫?yīng)毩⑦\行的Java應(yīng)用程序,這篇文章主要給大家介紹了關(guān)于JavaSE程序邏輯控制實現(xiàn)的相關(guān)資料,需要的朋友可以參考下2024-04-04mybatis如何使用Java8的日期LocalDate和LocalDateTime詳解
這篇文章主要給大家介紹了關(guān)于mybatis如何使用Java8的日期LocalDate和LocalDateTime的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2017-09-09springboot mybatis druid配置多數(shù)據(jù)源教程
這篇文章主要介紹了springboot mybatis druid配置多數(shù)據(jù)源教程,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-11-11java數(shù)據(jù)結(jié)構(gòu)之希爾排序
這篇文章主要為大家詳細(xì)介紹了java數(shù)據(jù)結(jié)構(gòu)之希爾排序的相關(guān)代碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-11-11