Tomcat和Spring中的事件機(jī)制深入講解
引言
最近在看tomcat源碼,源碼中出現(xiàn)了大量事件消息,可以說(shuō)整個(gè)tomcat的啟動(dòng)流程都可以通過(guò)事件派發(fā)機(jī)制串起來(lái),研究透了tomcat的各種事件消息,基本上對(duì)tomcat的啟動(dòng)流程也就有了一個(gè)整體的認(rèn)識(shí)。在這一基礎(chǔ)上,聯(lián)想到之前在看spring源碼過(guò)程中也存在不少事件相關(guān)知識(shí),于是想對(duì)這兩個(gè)框架中的事件派發(fā)機(jī)制做一個(gè)簡(jiǎn)單的總結(jié),加深理解。
事件機(jī)制原理其實(shí)比較簡(jiǎn)單,抽象來(lái)看的話,設(shè)計(jì)模式中的觀察者模式可以說(shuō)是最經(jīng)典的事件驅(qū)動(dòng)機(jī)制的體現(xiàn)了,觀察者和被觀察者就體現(xiàn)了事件監(jiān)聽(tīng)和事件派發(fā)的角色。還有各種MQ,其實(shí)也是事件機(jī)制的一種體現(xiàn)。
理解tomcat和spring中的事件機(jī)制之前,讓我們先從最基本的jdk中提供的事件機(jī)制開(kāi)始說(shuō)起。
JDK中的事件機(jī)制
JDK中對(duì)事件機(jī)制的各個(gè)角色提供了完善的抽象,主要包括3個(gè)角色:
EventObject(事件關(guān)注內(nèi)容):事件發(fā)布時(shí)需要關(guān)注的內(nèi)容。jdk中提供了EventObject接口。
EventListener(事件監(jiān)聽(tīng)者):事件監(jiān)聽(tīng)對(duì)象,也就是對(duì)EventObject感興趣的對(duì)象。jdk中提供了EventListener接口。
EventSource(事件源):發(fā)布事件的對(duì)象,可以在該對(duì)象中組冊(cè)EventListener,然后在特定的條件下發(fā)布EventObject給已經(jīng)注冊(cè)的EventListener。
事件的注冊(cè)與發(fā)布,需要這三個(gè)對(duì)象協(xié)同工作,可以通過(guò)下面的例子來(lái)說(shuō)明各個(gè)對(duì)象的作用:
首先是事件關(guān)注內(nèi)容對(duì)象MyEventObject,實(shí)現(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)聽(tīng)接口MyEventListener,繼承了EventListener,定義了一個(gè)myEvent接口用來(lái)發(fā)布事件,任何感興趣的監(jiān)聽(tīng)對(duì)象都可以實(shí)現(xiàn)該接口來(lái)監(jiān)聽(tīng)。
對(duì)MyEventObject感興趣的監(jiān)聽(tīng)者M(jìn)yEventListenerImpl,實(shí)現(xiàn)了MyEventListener接口,當(dāng)事件發(fā)布時(shí)會(huì)觸發(fā)myEvent事件并收到MyEventObject對(duì)象。
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ā)布源對(duì)象MyEventSource,它可以注冊(cè)多個(gè)事件監(jiān)聽(tīng)對(duì)象,任何實(shí)現(xiàn)了MyEventListener接口的監(jiān)聽(tīng)對(duì)象都可以注冊(cè),內(nèi)部通過(guò)一個(gè)Set來(lái)存儲(chǔ)感興趣的監(jiān)聽(tīng)對(duì)象,并在合適的時(shí)機(jī)會(huì)發(fā)布消息并通知所有監(jiān)聽(tīng)對(duì)象。
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);
}
}
}
之后可以通過(guò)一個(gè)啟動(dòng)類來(lái)注冊(cè)并觸發(fā)事件:
public static void main(String[] args) {
MyEventSource myEventSource = new MyEventSource();
MyEventListenerImpl myEventListenerImpl = new MyEventListenerImpl();
myEventSource.addListener(myEventListenerImpl);
myEventSource.pushEvent();
}
MyEventObject定義了感興趣的內(nèi)容,MyEventListenerImpl是對(duì)MyEventObject感興趣的監(jiān)聽(tīng)者,MyEventSource會(huì)發(fā)布MyEventObject給所有組冊(cè)的監(jiān)聽(tīng)者,最后通過(guò)一個(gè)main來(lái)啟動(dòng)整個(gè)流程。
明白了jdk中對(duì)事件機(jī)制的定義,再來(lái)看看tomcat和spring中的事件機(jī)制。
Tomcat的事件機(jī)制
tomcat的事件機(jī)制也離不開(kāi)EventObject、EventListener以及EventSource三個(gè)對(duì)象,只不過(guò)在此基礎(chǔ)上提供了更加抽象和便捷的操作。這里我挑選tomcat的生命周期接口對(duì)象Lifecycle來(lái)講解整個(gè)事件發(fā)布流程:
首先還是EventObject對(duì)象LifecycleEvent,這里只列出了核心代碼。它的主要參數(shù)是Lifecycle,Lifecycle中定義了tomcat各個(gè)階段的名稱:before_init、after_init、start等等,是事件監(jiān)聽(tīng)者感興趣的對(duì)象。
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)聽(tīng)接口LifecycleListener,定義了lifecycleEvent方法用來(lái)傳遞監(jiān)聽(tīng)者感興趣的LifecycleEvent對(duì)象,監(jiān)聽(tīng)者使用LifecycleEvent參數(shù)用來(lái)在tomcat的各個(gè)階段處理進(jìn)行相應(yīng)處理。這些感興趣的對(duì)象包括下面這些類:

這里使用ContextConfig類為例,可以看到它實(shí)現(xiàn)了LifecycleListener接口。這個(gè)類在解析server.xml的時(shí)候用來(lái)監(jiān)聽(tīng)StandardContext的各個(gè)階段的事件,并做出相應(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是我們需要了解的主要對(duì)象,它是監(jiān)聽(tīng)對(duì)象的一個(gè)管理類,原理其實(shí)和上面的例子差不多,對(duì)應(yīng)了MyEventSource類的部分功能,方便EventSource類來(lái)管理監(jiān)聽(tīng)對(duì)象。它把對(duì)監(jiān)聽(tīng)對(duì)象的添加移除以及發(fā)布事件幾個(gè)操作進(jìn)行了統(tǒng)一管理,避免EventSource類中出現(xiàn)太多管理監(jiān)聽(tīng)對(duì)象的邏輯。
public final class LifecycleSupport {
//......
//監(jiān)聽(tīng)對(duì)象集合
private LifecycleListener listeners[] = new LifecycleListener[0];
private final Object listenersLock = new Object(); // Lock object for changes to listeners
//添加監(jiān)聽(tīng)對(duì)象
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)聽(tīng)對(duì)象
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)聽(tīng)對(duì)象
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就簡(jiǎn)單多了,只需要調(diào)用LifecycleSupport的各個(gè)方法就可以了:
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ā)布事件時(shí)調(diào)用fireLifecycleEvent方法就可以發(fā)布事件:
fireLifecycleEvent(Lifecycle.CONFIGURE_STOP_EVENT, null);
tomcat事件機(jī)制就是在之前的例子上抽出了一個(gè)LifecycleSupport類來(lái)方便管理監(jiān)聽(tīng)對(duì)象的各種操作,這是一個(gè)可以借鑒的地方,其他差別并不大。再來(lái)看看spring中對(duì)事件機(jī)制的處理。
Spring的事件機(jī)制
spring中的事件機(jī)制原理也是一樣的,只是相對(duì)來(lái)說(shuō)實(shí)現(xiàn)上稍微復(fù)雜一點(diǎn)。還是通過(guò)相同的角度來(lái)看這個(gè)問(wèn)題。
首先是EventObject,spring里面的主要實(shí)現(xiàn)是ApplicationEvent:

這里通過(guò)ContextStartedEvent類來(lái)查看EventObject,它關(guān)注的對(duì)象是ApplicationContext,是spring容器在啟動(dòng)時(shí)觸發(fā)的事件對(duì)象:
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)聽(tīng)接口ApplicationListener,定義了onApplicationEvent方法用來(lái)傳遞監(jiān)聽(tīng)者感興趣的ApplicationEvent對(duì)象,監(jiān)聽(tīng)者使用ApplicationEvent參數(shù)用來(lái)在Context的各個(gè)階段處理進(jìn)行相應(yīng)處理。
如果我們需要在容器啟動(dòng)后進(jìn)行相應(yīng)處理,那么我們可以在業(yè)務(wù)類中實(shí)現(xiàn)ApplicationListener接口,在事件發(fā)生時(shí)就會(huì)發(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ā)布這些事件的呢?是不是也有一個(gè)類似tomcat中LifecycleSupport一樣的類呢?通過(guò)查看源碼可以發(fā)現(xiàn)發(fā)現(xiàn),ApplicationContext容器在初始化階段會(huì)調(diào)用refresh()方法,這其中又調(diào)用了
finishRefresh()方法,這其中調(diào)用了publishEvent(new ContextRefreshedEvent(this))方法,發(fā)布了ContextRefreshedEvent這一對(duì)象。
protected void finishRefresh() {
//......
// Publish the final event.
publishEvent(new ContextRefreshedEvent(this));
}
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
//......
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
//......
}
publishEvent方法通過(guò)調(diào)用一個(gè)默認(rèn)的多播器SimpleApplicationEventMulticaster的multicastEvent方法來(lái)發(fā)布各種事件:
SimpleApplicationEventMulticaster
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
//通過(guò)getApplicationListeners獲取了所有監(jiān)聽(tīng)器,然后通過(guò)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);
}
也就是說(shuō)在spring容器中發(fā)布ApplicationListener所關(guān)注的對(duì)象是通過(guò)SimpleApplicationEventMulticaster這個(gè)類來(lái)管理的,和tomcat中LifecycleSupport的功能類似,只是在實(shí)現(xiàn)上有略微差別。
最后提一句,在spring中你也可以自己發(fā)布各種事件,調(diào)用ApplicationContext的publishEvent方法即可。
applicationContext.publishEvent(new ApplicationEvent(new String("事件發(fā)布")) { });
總結(jié)
這篇文章對(duì)Java的事件機(jī)制在tomcat以及spring框架中的實(shí)現(xiàn)做了一個(gè)簡(jiǎn)單總結(jié)和對(duì)比,你需要知道以下幾點(diǎn):
- JDK中定義了EventObject和EventListener兩個(gè)接口,奠定了事件機(jī)制的基礎(chǔ)。
- Tomcat額外提供了一個(gè)support類來(lái)對(duì)監(jiān)聽(tīng)器的添加刪除以及發(fā)布進(jìn)行管理。
- Spring容器內(nèi)部通過(guò)SimpleApplicationEventMulticaster來(lái)發(fā)布各個(gè)事件,用戶可以通過(guò)實(shí)現(xiàn)ApplicationListener接口來(lái)監(jiān)聽(tīng)自己感興趣的容器事件。
希望你通過(guò)這篇文章的學(xué)習(xí)可以對(duì)Java的事件機(jī)制有一個(gè)更深刻的認(rèn)識(shí),在實(shí)現(xiàn)自己的事件機(jī)制時(shí)有可以借鑒以及改進(jìn)的地方。
好了,以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
JavaSE程序邏輯控制實(shí)現(xiàn)詳細(xì)圖文教程
JavaSE是為了開(kāi)發(fā)桌面應(yīng)用程序和控制臺(tái)應(yīng)用程序而設(shè)計(jì)的,使用JavaSE可以編寫?yīng)毩⑦\(yùn)行的Java應(yīng)用程序,這篇文章主要給大家介紹了關(guān)于JavaSE程序邏輯控制實(shí)現(xiàn)的相關(guān)資料,需要的朋友可以參考下2024-04-04
mybatis如何使用Java8的日期LocalDate和LocalDateTime詳解
這篇文章主要給大家介紹了關(guān)于mybatis如何使用Java8的日期LocalDate和LocalDateTime的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-09-09
springboot mybatis druid配置多數(shù)據(jù)源教程
這篇文章主要介紹了springboot mybatis druid配置多數(shù)據(jù)源教程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11
java數(shù)據(jù)結(jié)構(gòu)之希爾排序
這篇文章主要為大家詳細(xì)介紹了java數(shù)據(jù)結(jié)構(gòu)之希爾排序的相關(guān)代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-11-11
Java多態(tài)成員訪問(wèn)的特點(diǎn)是什么?
在上一篇文章中介紹了方法重載和方法重寫的區(qū)別,但是在多態(tài)情況下發(fā)現(xiàn)程序的執(zhí)行結(jié)果和我們預(yù)期的不太一樣,這篇將繼續(xù)介紹多態(tài)場(chǎng)景下,Java成員訪問(wèn)的特點(diǎn),需要的朋友可以參考下2021-06-06

