Filter

节选部分源码、源码版本 Tomcat8.5

说明

  • filter 是 Servlet 规范

  • filter 是在 <请求进入容器后>,执行 Servlet.service方法之前执行

Filter相关接口

public interface Filter {
// 初始化
void init(FilterConfig filterConfig) throws ServletException;
// 过滤器方法
void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException;
// 销毁方法
void destroy();
}
// 过滤器链接口
public interface FilterChain {
void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException;
}
  • 重点关注:初始化方法、过滤器方法

初始化

// org.apache.catalina.core.StandardContext
@Override
protected synchronized void startInternal() throws LifecycleException {
// 准备好环境
if (ok) {
if (!filterStart()) {
ok = false;
}
}
} private HashMap<String, ApplicationFilterConfig> filterConfigs =
new HashMap<>();
public boolean filterStart() {
// Instantiate and record a FilterConfig for each defined filter
boolean ok = true;
synchronized (filterConfigs) {
filterConfigs.clear();
// 从 web.xml 解析的 filter 数据中添加到 Map集合中
for (Entry<String,FilterDef> entry : filterDefs.entrySet()) {
// <filter-name> 的值
String name = entry.getKey();
try {
// 此处调用 Filter.init 方法,之后保存
ApplicationFilterConfig filterConfig =
new ApplicationFilterConfig(this, entry.getValue());
filterConfigs.put(name, filterConfig);
} catch (Throwable t) {
ok = false;
}
}
}
return ok;
}
  • 在解析完项目后,启动该项目时调用:startInternal 方法
  • 调用 filterStart 方法,初始化 Filter,保存 Filter到HashMap中

接收到请求的流程

  • 收到请求后根据 请求路径,找到相应的过滤器链,执行 doFilter 方法
  • 调用目标方法
// org.apache.catalina.core.StandardWrapperValve
// 节选部分代码
@Override
public final void invoke(Request request, Response response) {
// 创建执行Filter链
ApplicationFilterChain filterChain =
ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
// 调用链
if ((servlet != null) && (filterChain != null)) {
filterChain.doFilter(request.getRequest(),
response.getResponse());
}
// 释放掉 filterChain
if (filterChain != null) {
filterChain.release();
}
}

Line7:ApplicationFilterFactory.createFilterChain

// org.apache.catalina.core.ApplicationFilterFactory
public static ApplicationFilterChain createFilterChain(
ServletRequest request, Wrapper wrapper, Servlet servlet) { // 判断 servlet 是否存在
if (servlet == null) { return null; } ApplicationFilterChain filterChain = null;
if (request instanceof Request) {
Request req = (Request) request;
if (Globals.IS_SECURITY_ENABLED) {
filterChain = new ApplicationFilterChain();
} else {
// 判断 request 是否已经保存
filterChain = (ApplicationFilterChain) req.getFilterChain();
if (filterChain == null) {
filterChain = new ApplicationFilterChain();
req.setFilterChain(filterChain);
}
}
} else {
// Request dispatcher in use
filterChain = new ApplicationFilterChain();
} filterChain.setServlet(servlet);
filterChain.setServletSupportsAsync(wrapper.isAsyncSupported()); // 从 Context 中获取所有的 FilterMap 映射信息
StandardContext context = (StandardContext) wrapper.getParent();
FilterMap filterMaps[] = context.findFilterMaps(); // 没有配置 filter,返回 null
if ((filterMaps == null) || (filterMaps.length == 0))
return (filterChain); // Acquire the information we will need to match filter mappings
DispatcherType dispatcher =
(DispatcherType) request.getAttribute(Globals.DISPATCHER_TYPE_ATTR); String requestPath = null;
// 获取请求 路径:index.html
Object attribute = request.getAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR);
if (attribute != null){
requestPath = attribute.toString();
} String servletName = wrapper.getName(); for (int i = 0; i < filterMaps.length; i++) {
if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
continue;
}
// 匹配 url-pattern
if (!matchFiltersURL(filterMaps[i], requestPath))
continue;
// 根据 配置的 filter-name 找到 filterConfig
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
context.findFilterConfig(filterMaps[i].getFilterName());
if (filterConfig == null) {
continue;
}
filterChain.addFilter(filterConfig);
} // Add filters that match on servlet name second
for (int i = 0; i < filterMaps.length; i++) {
if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
continue;
}
if (!matchFiltersServlet(filterMaps[i], servletName))
continue;
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
context.findFilterConfig(filterMaps[i].getFilterName());
if (filterConfig == null) {
// FIXME - log configuration problem
continue;
}
filterChain.addFilter(filterConfig);
}
return filterChain;
}

LIne10:执行 ApplicationFilterChain.doFilter 方法

public final class ApplicationFilterChain implements FilterChain {
private int pos = 0;
private int n = 0; @Override
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
internalDoFilter(request,response);
} private void internalDoFilter(ServletRequest request, ServletResponse response) {
// Call the next filter if there is one
if (pos < n) {
ApplicationFilterConfig filterConfig = filters[pos++];
try {
Filter filter = filterConfig.getFilter();
if (request.isAsyncSupported() && "false".equalsIgnoreCase(
filterConfig.getFilterDef().getAsyncSupported())) {
request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE);
}
// 调用具体Filter 的 doFilter 方法。
filter.doFilter(request, response, this);
} catch (IOException | ServletException | RuntimeException e) {
throw e;
}
return;
} // 调用servlet实例
try {
if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
lastServicedRequest.set(request);
lastServicedResponse.set(response);
} if (request.isAsyncSupported() && !servletSupportsAsync) {
request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,
Boolean.FALSE);
}
servlet.service(request, response);
} catch (IOException | ServletException | RuntimeException e) {
throw e;
} finally {
if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
lastServicedRequest.set(null);
lastServicedResponse.set(null);
}
}
}
}
  • Line22,根据链中的具体的 Filter,调用 doFilter 方法
  • LIne40,调用 servlet.service(request, response)【此处 与 springmvc 整合入口】

问题

  • Request 为什么需要 保存 filterChain

req.setFilterChain(filterChain);

在 StandardWrapperValve 中会释放 filterChain

Tomcat中的Filter的更多相关文章

  1. 设计模式——责任链(结合Tomcat中Filter机制)

    设计模式:责任链模式 说责任链之前,先引入一个场景,假如规定学生请假小于或等于 2 天,班主任可以批准:小于或等于 7 天,系主任可以批准:小于或等于 10 天,院长可以批准:其他情况不予批准:以此为 ...

  2. CAS 在 Tomcat 中实现单点登录

    单点登录(Single Sign On , 简称 SSO )是目前比较流行的服务于企业业务整合的解决方案之一, SSO 使得在多个应用系统 中,用户只需要登录一次就可以访问所有相互信任的应用系统.CA ...

  3. 【IBM】使用 CAS 在 Tomcat 中实现单点登录

    来源: IBM Developer http://www.ibm.com/developerworks/cn/opensource/os-cn-cas/ 张 涛 (zzhangt@cn.ibm.com ...

  4. 使用 CAS 在 Tomcat 中实现单点登录

    单点登录(Single Sign On , 简称 SSO )是目前比较流行的服务于企业业务整合的解决方案之一, SSO 使得在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统.CAS ...

  5. Spring MVC中各个filter的用法

    转载:http://blog.csdn.net/qyp1314/article/details/42023725 Spring MVC中各个filter的用法 2014-12-19 09:08 105 ...

  6. Spring Boot启动过程(六):内嵌Tomcat中StandardHost与StandardContext的启动

    看代码有助于线上出现预料之外的事的时候,不至于心慌... StandardEngine[Tomcat].StandardHost[localhost]的启动与StandardEngine不在同一个线程 ...

  7. Tomcat中server.xml配置详解(2)

    Tomcat中配置文件详解 Server.xml配置文件说明,以及Tomcat组件的说明 Tomcat服务器是由一系列可以配置的组件构成,其中核心组件是Catalina Servlet,它是最顶层组件 ...

  8. Spring Boot启动过程(六):内嵌Tomcat中StandardHost、StandardContext和StandardWrapper的启动

    看代码有助于线上出现预料之外的事的时候,不至于心慌... StandardEngine[Tomcat].StandardHost[localhost]的启动与StandardEngine不在同一个线程 ...

  9. Tomcat中Pipeline

    Pipeline 节选部分源码.源码版本 Tomcat8.5 处理模式 Pipeline--Valve是一种责任链模式,它和普通责任链模式有两点区别: 每个Pipeline都是有特定的Valve,而且 ...

随机推荐

  1. varchar(n)跟varchar(max)的区别

    本文来源于翁舒航的博客,点击即可跳转原文观看!!!(被转载或者拷贝走的内容可能缺失图片.视频等原文的内容) 若网站将链接屏蔽,可直接拷贝原文链接到地址栏跳转观看,原文链接:https://www.cn ...

  2. Java https认证的坑

    https单向认证的服务端证书不是权威机构颁发的,网上找了点代码不对https证书进行认证后,报如下异常 javax.net.ssl.SSLHandshakeException: Received f ...

  3. CodeForces 606A(水)

    这道题之前没注意到at least,审题不仔细啊,两个问题解法还是有些许区别的 有at least的 #include <iostream> #include <string> ...

  4. The configuration section 'system.serviceModel' cannot be read because it is missing a section decla

    将Asp.Net 2.0的Web Site搭建在IIS7(7.5)上时,运行出现500.19错误, 错误提示为 The configuration section 'system.serviceMod ...

  5. 从BASE理论到CAP理论

    BASE理论面向的是大型高可用可扩展的分布式系统,和传统事务的CID特性是相反的,它完全不同于ACID的强一致性模型,而是提出通过牺牲强一致性来获得可用性,并允许数据在一段时间内是不一致的,但最终达到 ...

  6. Oracle数据库采用数据泵方式导入导出数据

    特别说明:Oralce的数据泵导入导出技术只能用在数据库服务器上,在只有客户端的机器上是无法使用数据泵技术的. 1.创建备份文件目录  mkdir d:\dmp 2.在Oralce中注册该目录,将目录 ...

  7. Fast Walsh-Hadamard Transform——快速沃尔什变换(二)

    上次的博客有点模糊的说...我把思路和算法实现说一说吧... 思路 关于快速沃尔什变换,为了方便起见,我们采用线性变换(非线性变换不会搞). 那么,就会有一个变化前各数值在变换后各处的系数,即前一篇博 ...

  8. 用一个div模拟textarea并实现高度自适应

    1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="U ...

  9. 微服务架构之spring boot admin

    Spring boot admin是可视化的监控组件,依赖spring boot actuator收集各个服务的运行信息,通过spring boot actuator可以非常方便的查看每个微服务的He ...

  10. 验证两台机器已经建立的ssh互信

    1.expect方法 #!/bin/bash checkTrust() { expect -c ' set timeout 2; spawn ssh $1 "expr 12345678 + ...