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 }
  • 相关阅读:
    【CSDN博客之星评选】我为什么坚持写博客
    关于纯css布局的概况
    IIS服务器下301跳转是怎么样实现的?
    如何使用数据库保存session的方法简介
    PHP如何通过SQL语句将数据写入MySQL数据库呢?
    PHP中文函数顺序排列一数组且其序数不变
    angular实时显示checkbox被选中的元素
    oracle查询正在执行的语句以及正被锁的对象
    angular中ng-repeat去重
    接口自动化测试框架--http请求的get、post方法的实现
  • 原文地址:https://www.cnblogs.com/hvicen/p/6004187.html
Copyright © 2011-2022 走看看