引言
最近在看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的事件機制有一個更深刻的認識,在實現自己的事件機制時有可以借鑒以及改進的地方。