Tomcat对Servlet规范的Filter及Listener实现
加载Servlet的类不等于创建Servlet实例,Tomcat先加载Servlet的类,然后还得在Java堆创建Servlet实例。
一个Web应用里往往有多个Servlet,而在Tomcat中一个Web应用对应一个Context容器,即一个Context容器需管理多个Servlet实例。
但Context容器并不直接持有Servlet实例,而是通过子容器Wrapper管理Servlet,可以把Wrapper容器看作Servlet的包装。
为何需要Wrapper?Context容器直接维护一个Servlet数组还不满足?
Servlet不仅是个类实例,还有相关配置信息,比如URL映射、初始化参数,因此设计出了一个包装器,把Servlet本身和它相关的数据包起来。
管理好Servlet就够了吗?
Listener和Filter也是Servlet规范,Tomcat也要创建它们的实例,在合适时机调用它们的方法。
Servlet管理
Tomcat用Wrapper容器管理Servlet
protected volatile Servlet instance = null;
它拥有一个Servlet实例,Wrapper#loadServlet实例化Servlet:
public synchronized Servlet loadServlet() throws ServletException {Servlet servlet;// 1. 创建Servlet实例servlet = (Servlet) instanceManager.newInstance(servletClass); // 2.调用了Servlet#init【Servlet规范要求】initServlet(servlet);return servlet;
}
为加快系统启动速度,一般采取资源延迟加载,所以Tomcat默认情况下Tomcat在启动时不会加载你的Servlet,除非把Servlet loadOnStartup参数置true。
虽然Tomcat在启动时不会创建Servlet实例,但会创建Wrapper容器。当有请求访问某Servlet,才会创建该Servlet实例。
Servlet是被谁调用呢?
Wrapper作为一个容器组件,有自己的Pipeline和BasicValve,其BasicValve为StandardWrapperValve。
当请求到来,Context容器的BasicValve会调用Wrapper容器中Pipeline中的第一个Valve,然后调用到StandardWrapperValve:
public final void invoke(Request request, Response response) {// 1.实例化Servletservlet = wrapper.allocate();// 2.给当前请求创建一个Filter链ApplicationFilterChain filterChain =ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);// 3. 调用这个Filter链,Filter链中的最后一个Filter会调用ServletfilterChain.doFilter(request.getRequest(), response.getResponse());}
StandardWrapperValve的invoke就三步:
- 创建Servlet实例
- 给当前请求创建一个Filter链
- 调用Filter链
为何要给每个请求创建Filter链
每个请求的请求路径不同,而Filter都有相应路径映射,因此不是所有Filter都需要处理当前请求,要根据请求路径选择特定的一些Filter。
为何没调用Servlet#service
Filter链的最后一个Filter会负责调用Servlet。
Filter管理
跟Servlet一样,Filter也可在web.xml
配置。
但Filter的作用域是整个Web应用,因此Filter的实例维护在Context容器:Map里存的是filterDef(filter定义),而非filter类实例
Filter链存活期很短,它跟每个请求对应。一个新请求来了,就动态创建一个Filter链,请求处理完,Filter链就被回收。
public final class ApplicationFilterChain implements FilterChain {// Filter链的Filter数组private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0];// Filter链的当前的调用位置private int pos = 0;// Filter总数private int n = 0;// 每个Filter链最终要调用的Servletprivate Servlet servlet = null;public void doFilter(ServletRequest req, ServletResponse res) {internalDoFilter(request,response);}private void internalDoFilter(ServletRequest req,ServletResponse res){if (pos < n) {ApplicationFilterConfig filterConfig = filters[pos++];Filter filter = filterConfig.getFilter();filter.doFilter(request, response, this);return;}servlet.service(request, response);}
internalDoFilter里会做个判断:
- 若当前Filter位置 < Filter数组长度,即Filter还没调完,就从Filter数组取下一个Filter,调用其doFilter
- 否则,说明已调用完所有Filter,该调用Servlet#service了。service方法是留给程序员实现业务逻辑的,比如CRUD
public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain){...//调用Filter的方法chain.doFilter(request, response);}
Filter#doFilter的FilterChain参数,就是Filter链。每个Filter#doFilter里必须调用Filter链的doFilter,而Filter链中保存当前Filter位置,会调用下一个Filter的doFilter方法,这样就能完成链式调用。
对应的filter是怎么注册到Servlet的呢?
filter是注册到Servlet容器中,Tomcat的StandardContext类中维护了一个Filter列表,所谓注册就是把你写的filter类实例加到这个列表。
Listener管理
Listener可以监听容器内部发生的事件:
- 生命状态的变化
比如Context容器启动和停止、Session的创建和销毁。 - 属性变化
比如Context容器某个属性值变了、Session的某个属性值变了以及新的请求来了
怎么添加监听器
在web.xml配置或注解添加,在监听器里实现业务逻辑。
Tomcat需读取配置文件,拿到监听器的类名,将它们实例化,并适时调用这些监听器方法。
Tomcat是通过Context容器来管理这些监听器的。Context容器将两类事件分开来管理,分别用不同的集合来存放不同类型事件的监听器:
//监听属性值变化的监听器
private List<Object> applicationEventListenersList = new CopyOnWriteArrayList<>();//监听生命事件的监听器
private Object applicationLifecycleListenersObjects[] = new Object[0];
剩下的事情就是触发监听器了,比如在Context容器的启动方法里,就触发了所有的ServletContextListener:// 1 拿到所有生命周期监听器
Object instances[] = getApplicationLifecycleListeners();for (int i = 0; i < instances.length; i++) {// 2 判断Listener的类型是否为ServletContextListenerif (!(instances[i] instanceof ServletContextListener))continue;// 3 触发Listener方法ServletContextListener lr = (ServletContextListener) instances[i];lr.contextInitialized(event);
}
这里的ServletContextListener接口是留给用户的扩展机制,用户可以实现该接口定义自己的监听器,监听Context容器的启停事件。
ServletContextListener跟Tomcat自己的生命周期事件LifecycleListener是不同的。LifecycleListener定义在生命周期管理组件中,由基类LifecycleBase统一管理。
可定制监听器监听Tomcat内部发生的各种事件:比如Web应用、Session级别或请求级别的。Tomcat中的Context容器统一维护了这些监听器,并负责触发。
Context组件通过自定义类加载器来加载Web应用,并实现了Servlet规范,直接跟Web应用打交道。
FAQ
Context容器分别用了CopyOnWriteArrayList和对象数组来存储两种不同的监听器,为什么要这样设计呢?
因为:
- 属性值变化listener能动态配置,所以用CopyOnWriteArray
写不会那么频繁,读取比较频繁 - 生命周期事件listener,不能动态改变,无线程安全问题
生命周期相关的类比如session一个用户分配一个,用完了就会销毁,用对象数组,可以适应增删改操作
Tomcat对Servlet规范的Filter及Listener实现相关推荐
- Servlet规范之Filter工作原理
概述 Filter是Servlet规范中的过滤器,Web服务器根据应用程序配置文件设置的过滤规则进行检查,若客户请求满足过滤规则,则对客户请求/响应进行拦截,对请求头和请求数据进行检查或改动,并依次通 ...
- Javaweb核心之servlet规范过滤器-----Filter
2 Servlet规范中的过滤器-Filter 2.1 过滤器入门 2.1.1 过滤器概念及作用 过滤器--Filter,它是JavaWeb三大组件之一.另外两个是Servlet和Listener. ...
- java filter与servlet,Servlet中的Filter和Listener之间的区别(Java EE)
There are Filters and Listeners functionality in Servlet. I want to know exact difference between Fi ...
- 在servlet(或者filter或者listener)中使用spring的IOC容器
web.xml中的加载顺序为:listener >> filter >> servlet >> spring.其中filter的执行顺序是filter- mappi ...
- Servlet、Filter、Listener总结
servlet规范提供了一组标准的servlet api.servlet容器就是servlet规范的实现. 1.In Action (1)写一个类继承HttpServlet: (2)重写其中的方法. ...
- 深入理解Tomcat和Jetty源码之第二篇servlet规范和servlet容器
深入理解Tomcat和Jetty源码之第二篇servlet规范和servlet容器 思维导图总览 这篇推送主要讲servlet的规范和什么是servlet容器? 1.先来讲讲servlet规范: 2. ...
- Java Web 项目入门指南(http、Servlet、Request、Response、ServletContext、会话技术[cookie、session]、Filter、Listener)
概述 web 服务器.项目.资源概述 web 服务器:可以被浏览器访问到的服务器 常见的 web 服务器: tomcat:中小型的服务器软件,免费开源,支持 JSP 和 Servlet apache ...
- springboot_4 spring boot 使用servlet,filter,listener和interceptor
上一篇我们学习了 spring boot 利用Controller响应数据与响应页面. 一般的Web开发使用 Controller 基本上可以完成大部分需求,但是有的时候我们还是会用到 Servlet ...
- SpringBoot(四)-- 整合Servlet、Filter、Listener
SpringBoot中有两种方式可以添加 Servlet.Filter.Listener. 1.代码注册 通过ServletRegistrationBean. FilterRegistrationBe ...
最新文章
- 限流算法之漏桶算法、令牌桶算法
- android studio 2.1 ndk,Android studio 2.1编辑器(CLint)无法找到使用原生(ndk)插件的模块的标题...
- mybatis返回map键值对_mybatis返回map结果集怎么配置
- AWS Elemental推出新一代基于云的直播视频服务
- sap.ca.ui.model.format.NumberFormat format logic for Globalization
- php while结束循环吗,php while循环退不出是什么有关问题
- 失败的人生图片_人到中年,做事失败了,很可能是遇到了以下五种情况
- 完成一个YUV文件播放器
- 哦~最重要的产品链接忘了发了
- 最全干货回顾,一文看尽亚马逊 re:Invent 2020全貌
- MYSQL——操作数据表中的记录
- 面向服务架构(SOA)和企业服务总线(ESB)
- C#反射获取 所有字段 及 私有字段
- android 蓝牙耳机插拔,一种插拔式蓝牙耳机的制作方法
- ARGB颜色转16进制颜色
- 基本积分表的联想记忆
- ROSDUCT:通过rosbridge在本地公开远程ROS主题、服务和参数
- xiuno论坛开发模式怎么调整
- 精品基于SSM的在线租房网站
- PyCharm配置SSH和SFTP连接远程服务器