Tomcat與Spring中的事件機制詳解

引言

最近在看tomcat源碼,源碼中出現了大量事件消息,能夠說整個tomcat的啓動流程均可以經過事件派發機制串起來,研究透了tomcat的各類事件消息,基本上對tomcat的啓動流程也就有了一個總體的認識。在這一基礎上,聯想到以前在看spring源碼過程當中也存在很多事件相關知識,因而想對這兩個框架中的事件派發機制作一個簡單的總結,加深理解。java

事件機制原理其實比較簡單,抽象來看的話,設計模式中的觀察者模式能夠說是最經典的事件驅動機制的體現了,觀察者和被觀察者就體現了事件監聽和事件派發的角色。還有各類MQ,其實也是事件機制的一種體現。spring

理解tomcat和spring中的事件機制以前,讓咱們先從最基本的jdk中提供的事件機制開始提及。設計模式

JDK中的事件機制

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

EventObject(事件關注內容):事件發佈時須要關注的內容。jdk中提供了EventObject接口。app

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

EventSource(事件源):發佈事件的對象,能夠在該對象中組冊EventListener,而後在特定的條件下發布EventObject給已經註冊的EventListener。ide

事件的註冊與發佈,須要這三個對象協同工做,能夠經過下面的例子來講明各個對象的做用:學習

首先是事件關注內容對象MyEventObject,實現了EventObject接口。eventName參數爲具體的事件關注內容this

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框架中的實現作了一個簡單總結和對比,你須要知道如下幾點:

  1. JDK中定義了EventObject和EventListener兩個接口,奠基了事件機制的基礎。
  2. Tomcat額外提供了一個support類來對監聽器的添加刪除以及發佈進行管理。
  3. Spring容器內部經過SimpleApplicationEventMulticaster來發布各個事件,用戶能夠經過實現ApplicationListener接口來監聽本身感興趣的容器事件。

但願你經過這篇文章的學習能夠對Java的事件機制有一個更深入的認識,在實現本身的事件機制時有能夠借鑑以及改進的地方。

相關文章
相關標籤/搜索