tomcat實現ServletContext的addListener方法的源碼解說(原創)


tomcat 8.0.36

知識點:

  • 動態監聽器有七類: 
  1. ServletContextAttributeListener
  2. ServletRequestListener
  3. ServletRequestAttributeListener
  4. HttpSessionIdListener
  5. HttpSessionAttributeListener
  6. HttpSessionListener
  7. ServletContextListener
  • 動態監聽器必須在啟動前完成,即使用ServletContainerInitializer或ServletContextListener進行動態添加。
  • 如果要動態添加ServletContextListener,只能使用ServletContainerInitializer進行。

 

 

ServletContext的addListener方法,是一個通過動態添加監聽器的方法。

傳入的參數必須是EventListener類型。

public <T extends EventListener> void addListener(T t)

 

添加的時機受到限制,必須在指定時機STARTING_PREP下才能添加。

if (!context.getState().equals(LifecycleState.STARTING_PREP)) {
    throw new IllegalStateException(
    sm.getString("applicationContext.addListener.ise",
    getContextPath())); }

STARTING_PREP的意思是啟動前,tomcat在啟動的時候有兩個狀態,一個是啟動前,一個是正式啟動,也就說在狀態變為正式啟動的時候,要把該添加的添加,不然就沒機會了。

tomcat在維持啟動前這個狀態下,調用servlet api的方法主要有兩個:

  1. ServletContainerInitializer#onStartup(Set<Class<?>> c, ServletContext ctx)
  2. ServletContextListener#contextInitialized(ServletContextEvent sce)

其實這兩個方法的作用都一樣,都能夠監聽ServletContext初始化事件,但用法上面有些不一樣,例如前者要比后者早觸發,前者的實現類需要放在一個模塊中,關於模塊和其它一些區別的地方不再敘述。

 

tomcat使用addApplicationEventListener和addApplicationLifecycleListener這兩個方法,區分存儲在兩個列表里。

if (t instanceof ServletContextAttributeListener
        || t instanceof ServletRequestListener
        || t instanceof ServletRequestAttributeListener
        || t instanceof HttpSessionIdListener
        || t instanceof HttpSessionAttributeListener) {
    context.addApplicationEventListener(t);
    match = true;
}

if (t instanceof HttpSessionListener || 
    (t instanceof ServletContextListener
      && newServletContextListenerAllowed)) { context.addApplicationLifecycleListener(t); match = true; }

 雖然傳入的參數類型限制在EventListener類型,但實際上動態添加的類型只有這七類:

  1. ServletContextAttributeListener
  2. ServletRequestListener
  3. ServletRequestAttributeListener
  4. HttpSessionIdListener
  5. HttpSessionAttributeListener
  6. HttpSessionListener
  7. ServletContextListener

其中ServletContextListener,是受到newServletContextListenerAllowed變量的限制。 

這個變量默認為true,在調用ServletContainerInitializer#onStartup方法時,仍然是true。

但在調用ServletContextListener#contextInitialized方法之前,這變量就會變成false,調用完后也一直保持着false。

也就是說,如果想通過動態添加監聽器ServletContextListener的方式,只能在ServletContainerInitializer#onStartup的方法中添加。

其實簡單的總結就是,ServletContextListener作為一個監聽ServletContext的初始化,不能在這時候再添加這樣的監聽器,有什么事沒做完的可以現在完成,非要搞什么推遲一下完成,是不是有點傻。

 

看到了么,最后那些未添加的監聽器,都會接受末日審判。 

if (match)
    return;

if (t instanceof ServletContextListener) {
    throw new IllegalArgumentException(
    sm.getString("applicationContext.addListener.iae.sclNotAllowed",
    t.getClass().getName()));
} else {
    throw new IllegalArgumentException(
    sm.getString("applicationContext.addListener.iae.wrongType",
    t.getClass().getName()));
}

 

 

完整源碼:

 1 public <T extends EventListener> void addListener(T t) {
 2     if (!context.getState().equals(LifecycleState.STARTING_PREP)) {
 3         throw new IllegalStateException(sm.getString("applicationContext.addListener.ise", getContextPath()));
 4     }
 5 
 6     boolean match = false;
 7     
 8     if (t instanceof ServletContextAttributeListener
 9             || t instanceof ServletRequestListener
10             || t instanceof ServletRequestAttributeListener
11             || t instanceof HttpSessionIdListener
12             || t instanceof HttpSessionAttributeListener) {
13         context.addApplicationEventListener(t);
14         match = true;
15     }
16 
17     if (t instanceof HttpSessionListener || (t instanceof ServletContextListener && newServletContextListenerAllowed)) {
18         context.addApplicationLifecycleListener(t);
19         match = true;
20     }
21 
22     if (match)
23         return;
24 
25     if (t instanceof ServletContextListener) {
26         throw new IllegalArgumentException(sm.getString("applicationContext.addListener.iae.sclNotAllowed", t.getClass().getName()));
27     } else {
28         throw new IllegalArgumentException(sm.getString("applicationContext.addListener.iae.wrongType", t.getClass().getName()));
29     }
30 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM