tomcat实现ServletContext的addListener方法的源码解说(原创)
tomcat 8.0.36
知识点:
- 动态监听器有七类:
- ServletContextAttributeListener
- ServletRequestListener
- ServletRequestAttributeListener
- HttpSessionIdListener
- HttpSessionAttributeListener
- HttpSessionListener
- 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的方法主要有两个:
- ServletContainerInitializer#onStartup(Set<Class<?>> c, ServletContext ctx)
- 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类型,但实际上动态添加的类型只有这七类:
- ServletContextAttributeListener
- ServletRequestListener
- ServletRequestAttributeListener
- HttpSessionIdListener
- HttpSessionAttributeListener
- HttpSessionListener
- 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()));
}
完整源码:
public <T extends EventListener> void addListener(T t) {
if (!context.getState().equals(LifecycleState.STARTING_PREP)) {
throw new IllegalStateException(sm.getString("applicationContext.addListener.ise", getContextPath()));
}
boolean match = false;
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;
}
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()));
}
}
tomcat实现ServletContext的addListener方法的源码解说(原创)的更多相关文章
- Scala 深入浅出实战经典 第41讲:List继承体系实现内幕和方法操作源码揭秘
Scala 深入浅出实战经典 第41讲:List继承体系实现内幕和方法操作源码揭秘 package com.parllay.scala.dataset /** * Created by richard ...
- [Java源码解析] -- String类的compareTo(String otherString)方法的源码解析
String类下的compareTo(String otherString)方法的源码解析 一. 前言 近日研究了一下String类的一些方法, 通过查看源码, 对一些常用的方法也有了更透彻的认识, ...
- Pycharm中查看方法的源码
方法1.鼠标放在函数上,Ctrl+B,看源码 方法2.将光标移动至要查看的方法处,按住ctrl 键,点击鼠标左键,即可查看该方法的源码.
- 如何查看laravel门脸类包含方法的源码
以Route门脸类为例,我们定义路由时使用的就是Route门脸类,例如我们在web.php中定义的路由 use Illuminate\Support\Facades\Route; Route::get ...
- 《JAVA高并发编程详解》-Thread start方法的源码
Thread start方法的源码:
- Tomcat各个版本的下载地址包括源码
Tomcat各个版本的下载地址包括源码: http://archive.apache.org/dist/tomcat **************** 选择版本 **************** ** ...
- HttpServlet中service方法的源码解读
前言 最近在看<Head First Servlet & JSP>这本书, 对servlet有了更加深入的理解.今天就来写一篇博客,谈一谈Servlet中一个重要的方法-- ...
- beego 0.9.0 中智能路由AutoRouter的使用方法及源码解读
了解beego的开发者肯定知道,beego的路由设计来源于sinatra,原来是不支持自动路由的,每一个路由都要自己配置的,如: type MainController struct { beego. ...
- Tomcat 调优之从 Linux 内核源码层面看 Tcp backlog
前两天看到一群里在讨论 Tomcat 参数调优,看到不止一个人说通过 accept-count 来配置线程池大小,我笑了笑,看来其实很多人并不太了解我们用的最多的 WebServer Tomcat,这 ...
随机推荐
- 【随记】Hello World小记
今天装Python,如下: 突然想到,到现在,我已经数不清写过多少遍Hello World了. 最早是初一学VB的时候,用Label1在Form1上画一个,然后修改Caption属性为“Hello W ...
- C语言采用文本方式和二进制方式打开文件的区别分析
稍微了解C程序设计的人都知道,文本文件和二进制文件在计算机上面都是以0,1存储的,那么两者怎么还存在差别呢?对于编程人员来说,文本文件和二进制文件就是一个声明,指明了你应该以什么方式(文本方式/二进制 ...
- 用户图形界面(GUI)学习笔记(一)——Swing与AWT
一.Swing与AWT 对象窗口工具箱(Abstact Window Toolkit,AWT),是JAVA1.0刚出现的时候,包含的一个GUI设计类库.它将处理用户界面元素的任务委派给每个目标平台(W ...
- JS中innerHTML,innerText,value
一·.JS初学者易混淆的问题:innerHTML,innerText,value(他们和JQ的区别:JS→value,JQ→value()) 1.getElementById("a" ...
- LINUX btmp 日志(lastb 命令)
Linux下/var/log/btmp文件: 今天查看了一下服务器,发现/var/log/btmp日志文件比较大,搜索一下,此文件是记录错误登录的日志,就是说有很多人试图使用密码字典登录ssh服务,此 ...
- 要做linux运维工程师的朋友,必须要掌握以下几个工具才行 ...
要做linux运维工程师的朋友,必须要掌握以下几个工具才行 ... [复制链接] 发表于 2013-12-13 15:59 | 来自 51CTO网页 [只看他] 楼主 本人 ...
- [Asp.net 开发系列之SignalR篇]专题三:使用SignalR实现聊天室的功能
一.引言 在前一篇文章中,我向大家介绍了如何实现实现端对端聊天的功能的,在这一篇文章中将像大家如何使用SignalR实现群聊这样的功能. 二.实现思路 要想实现群聊的功能,首先我们需要创建一个房间,然 ...
- MySql学习(MariaDb)
资料 http://www.cnblogs.com/lyhabc/p/3691555.html http://www.cnblogs.com/lyhabc/p/3691555.html MariaDb ...
- RCP:eclipse的DEBUG机制
Eclipse debug文档翻译 运行一个程序需要添加launch configurable,在自定义launch configuration的时候会指定模式,比如run,debug,profile ...
- js下的面向对象
本文记录了一种Javascript的面向对象方法及原理理解,示例代码如下: //构造函数 var MClass = function(value1, value2) { this.member = & ...