zoukankan      html  css  js  c++  java
  • 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 }
  • 相关阅读:
    【Leetcode】23. Merge k Sorted Lists
    【Leetcode】109. Convert Sorted List to Binary Search Tree
    【Leetcode】142.Linked List Cycle II
    【Leetcode】143. Reorder List
    【Leetcode】147. Insertion Sort List
    【Leetcode】86. Partition List
    jenkins 配置安全邮件
    python 发送安全邮件
    phpstorm 同步远程服务器代码
    phpUnit 断言
  • 原文地址:https://www.cnblogs.com/hvicen/p/6004187.html
Copyright © 2011-2022 走看看