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,这 ...
随机推荐
- php数字补零的两种方法
在php中有两个函数——至少有两个是否有其他的我还不知道,能够实现数字补零,str_pad(),sprintf()详细如下 str_pad顾名思义这个函数是针对字符串来说的这个可以对指定的字符串填补任 ...
- MyEclipse调用Matlab打包函数
本文部分内容参考了http://www.360doc.com/content/15/1103/16/1180274_510463048.shtml 一.检查Java环境 对于已经装上JAVA环境的计算 ...
- Java使用velocity导出word
效果展示: 使用word编辑好模板
- Ubuntu之root权限的获取
方案一: Ubuntu的root密码在没有设置之前是随机的,即在每一次开机的时候他的密码都不同,但是由于在安装Ubuntu的时候需要建立一个账户,而这个招呼又属于admin组,因此它可以对root进行 ...
- 本人讲课时录制的Android应用开发技术教学视频
网盘地址:http://yun.baidu.com/pcloud/album/info?query_uk=1963923831&album_id=3523786484935252365 本人讲 ...
- Asp.net MVC4 与 Web Form 并存
Web Forms 与 MVC 的asp.net 基础架构是相同的.MVC 的路由机制并不只MVC 特有的,它与WebForm 也是共享相同的路由机制.Web Forms 的Http请求针 ...
- 配置WCF同时支持WSDL和REST,swaggerwcf生成文档
配置WCF同时支持WSDL和REST,SwaggerWCF生成文档 VS创建一个WCF工程,通过NuGet添加SwaggerWcf 创建完成后通过 程序包管理控制台 pm>Install-Pac ...
- 【腾讯Bugly干货分享】聊一聊微信“小程序”
本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57ecdf5ef03abecd43216fd0 Dev Club 是一个交流移动 ...
- dojo/dom-form
表单的处理在前端开发中一样意义非凡,dojo/dom-form模块提供了一系列方法来处理表单元素.比如: fieldToObject: 将一个表单字段转化成JavaScript原生类型,可能是stri ...
- 【网站国际化必备】Asp.Net MVC 集成Paypal(贝宝)快速结账 支付接口 ,附源码demo
开篇先给大家讲段历史故事,博主是湖北襄阳人.襄阳物华天宝,人杰地灵,曾用名襄樊.在2800多年的历史文化中出现了一代名相诸葛亮(卧龙),三国名士庞统(凤雏),魏晋隐士司马徽(水镜先生),唐代大诗人孟浩 ...