深入刨析tomcat 之---第8篇 how tomcat works 第11章 11.9应用程序,自定义Filter,及注册
writed by 张艳涛, 标签:全网独一份, 自定义一个Filter
起因:在学习深入刨析tomcat的学习中,第11章,说了调用过滤链的原理,但没有给出实例来,自己经过分析,给出来了一个FilterDemo
在startup包下,创建类ZytFilter,对于调用过滤器,之前不知道需要加上在doFilter方法里面加入filterChain.doFilter(servletRequest,servletResponse);
package com.zyt.tomcat.ex11.startup; import javax.servlet.*;
import java.io.IOException; public class ZytFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException { } @Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println(this.getClass().getName()+" 这个类执行了doFilter方法");
System.out.println(servletRequest);
filterChain.doFilter(servletRequest,servletResponse);
} @Override
public void destroy() { }
}
问个问题,如果doFilter方法不写标黄的语句?会发生什么呢?答案是不会调用servlet.service()方法,因为在applicationFilterChain的逻辑为,doFilter方法
找到所有的filter链成员,调用其中的一个其中的 filter.doFilter(request, response, this);如果不在自定义的dofilter方法中写接着调用下一个filter,那么这个方法中返回的是true,如果写了,接着调用下一个,如果下一个是空的话,则跳过调用调用filter,直接执行servlet.service()
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException { if( System.getSecurityManager() != null ) {
final ServletRequest req = request;
final ServletResponse res = response;
try {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedExceptionAction()
{
public Object run() throws ServletException, IOException {
internalDoFilter(req,res);
return null;
}
}
);
} catch( PrivilegedActionException pe) {
Exception e = pe.getException();
if (e instanceof ServletException)
throw (ServletException) e;
else if (e instanceof IOException)
throw (IOException) e;
else if (e instanceof RuntimeException)
throw (RuntimeException) e;
else
throw new ServletException(e.getMessage(), e);
}
} else {
internalDoFilter(request,response);
}
} private void internalDoFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException { // Construct an iterator the first time this method is called
if (this.iterator == null)
this.iterator = filters.iterator(); // Call the next filter if there is one
if (this.iterator.hasNext()) {
ApplicationFilterConfig filterConfig =
(ApplicationFilterConfig) iterator.next();
Filter filter = null;
try {
filter = filterConfig.getFilter();
support.fireInstanceEvent(InstanceEvent.BEFORE_FILTER_EVENT,
filter, request, response);
filter.doFilter(request, response, this);
support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
filter, request, response);
} catch (IOException e) {
if (filter != null)
support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
filter, request, response, e);
throw e;
} catch (ServletException e) {
if (filter != null)
support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
filter, request, response, e);
throw e;
} catch (RuntimeException e) {
if (filter != null)
support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
filter, request, response, e);
throw e;
} catch (Throwable e) {
if (filter != null)
support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
filter, request, response, e);
throw new ServletException
(sm.getString("filterChain.filter"), e);
}
return;
} // We fell off the end of the chain -- call the servlet instance
try {
support.fireInstanceEvent(InstanceEvent.BEFORE_SERVICE_EVENT,
servlet, request, response);
if ((request instanceof HttpServletRequest) &&
(response instanceof HttpServletResponse)) {
servlet.service((HttpServletRequest) request,
(HttpServletResponse) response);
} else {
servlet.service(request, response);
}
support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
servlet, request, response);
} catch (IOException e) {
support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
servlet, request, response, e);
throw e;
} catch (ServletException e) {
support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
servlet, request, response, e);
throw e;
} catch (RuntimeException e) {
support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
servlet, request, response, e);
throw e;
} catch (Throwable e) {
support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
servlet, request, response, e);
throw new ServletException
(sm.getString("filterChain.servlet"), e);
} }
如果.next为空,则不走if(){},进入servlet.service()方法执行
自定义Filter 过滤器
如果在web应用中添加过滤器,需要在容器的web.xml中定义filter 比如
<!--配置自己的过滤器-->
<filter>
<filter-name>MyFilter</filter-name>
<filter-class>com.zkj.filter.MyFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>MyFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
那么tomcat会通过xml解析器digest来解析,生成fitler对象
那么在how tomcat works 如果使用一个过滤器呢
package com.zyt.tomcat.ex11.startup; import com.zyt.tomcat.ex11.core.SimpleContextConfig;
import org.apache.catalina.*;
import org.apache.catalina.connector.http.HttpConnector;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.core.StandardWrapper;
import org.apache.catalina.deploy.FilterDef;
import org.apache.catalina.deploy.FilterMap;
import org.apache.catalina.loader.WebappLoader;
import org.apache.catalina.session.StandardManager; import java.io.IOException; public class BootStrap_ex11 {
public static void main(String[] args) {
System.setProperty("catalina.base", System.getProperty("user.dir"));
HttpConnector connector = new HttpConnector();
StandardWrapper wrapper1 = new StandardWrapper();
wrapper1.setName("Primitive");
wrapper1.setServletClass("PrimitiveServlet");
wrapper1.setDebug(2); StandardWrapper wrapper2 = new StandardWrapper();
wrapper2.setName("Modern");
wrapper2.setServletClass("ModernServlet");
wrapper2.setDebug(2); Wrapper wrapper3 = new StandardWrapper();
wrapper3.setName("SessionZYT");
wrapper3.setServletClass("SessionServletZYT");
//wrapper3.setDebug(2); Context context= new StandardContext();
context.setPath("/myApp");
context.setDocBase("myApp"); LifecycleListener listener = new SimpleContextConfig();
((Lifecycle) context).addLifecycleListener(listener); context.addChild(wrapper1);
context.addChild(wrapper2);
context.addChild(wrapper3);
/**
*手动添加一个filter*/
FilterDef filterDef = new FilterDef();
ZytFilter filter = new ZytFilter();
filterDef.setFilterName("ZytFilter");
filterDef.setFilterClass(filter.getClass().getName());
context.addFilterDef(filterDef);
// filterMap
FilterMap[] filterMaps = context.findFilterMaps();
FilterMap filterMap = new FilterMap();
filterMap.setFilterName("ZytFilter");
//filterMap.setDispatcher(String.valueOf(DispatcherType.REQUEST));
filterMap.setURLPattern("/*");
context.addFilterMap(filterMap); Loader loader = new WebappLoader();
context.setLoader(loader); context.addServletMapping("/Primitive","Primitive");
context.addServletMapping("/Modern","Modern");
context.addServletMapping("/myApp/SessionZYT","SessionZYT"); connector.setContainer(context);
// add a Manager
Manager manager = new StandardManager();
context.setManager(manager); try {
connector.initialize();
((Lifecycle) connector).start();
((Lifecycle) context).start();
System.in.read();
((Lifecycle) context).start();
} catch (LifecycleException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
绿色代码,是主要有两部分,组成,其中之一,新建一个FilterDef ,其二是 新建一个FilterMap,其中的语句就对于了xml中的标签
比如map中的,这就是其二定义,及其设置属性
<filter-mapping>
<filter-name>MyFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
代码里面是传入了一个filterdef 过滤器定义,也跟标签对应上了
filterDef.setFilterName("ZytFilter");
filterDef.setFilterClass(filter.getClass().getName());
<filter>
<filter-name>MyFilter</filter-name>
<filter-class>com.zkj.filter.MyFilter</filter-class>
</filter>
这其中设计到一个问题,我们新建的是一个def和map 如果生成filter的,和对规则进行匹配的呢?
先解析第一问题,
调用流程StandarContext的start()方法中有
*/
public synchronized void start() throws LifecycleException { // Create context attributes that will be required
if (ok) {
if (debug >= 1)
log("Posting standard context attributes");
postWelcomeFiles();
} // Configure and call application event listeners and filters
if (ok) {
if (!listenerStart())
ok = false;
}
if (ok) {
if (!filterStart())
ok = false;
} // Load and initialize all "load on startup" servlets
if (ok)
loadOnStartup(findChildren()); // Unbinding thread
unbindThread(oldCCL); // Set available status depending upon startup success
if (ok) {
if (debug >= 1)
log("Starting completed");
setAvailable(true);
} else {
log(sm.getString("standardContext.startFailed"));
try {
stop();
} catch (Throwable t) {
log(sm.getString("standardContext.startCleanup"), t);
}
setAvailable(false);
} // Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null); }
创建 filterConfig
public boolean filterStart() {
if (debug >= 1)
log("Starting filters");
// Instantiate and record a FilterConfig for each defined filter
boolean ok = true;
synchronized (filterConfigs) {
filterConfigs.clear();
Iterator names = filterDefs.keySet().iterator();
while (names.hasNext()) {
String name = (String) names.next();
if (debug >= 1)
log(" Starting filter '" + name + "'");
ApplicationFilterConfig filterConfig = null;
try {
filterConfig = new ApplicationFilterConfig
(this, (FilterDef) filterDefs.get(name));
filterConfigs.put(name, filterConfig);
} catch (Throwable t) {
log(sm.getString("standardContext.filterStart", name), t);
ok = false;
}
}
}
return (ok);
}
这个逻辑是看,filterDefs 的hashmap 有多少个def,每一个定义都新建一个 ApplicationFilterConfig对象,放进去了 filterConfigs hashmap中,
那么接下去的逻辑就是要创建filter,将filter 对象加入到chain链中
void addFilter(ApplicationFilterConfig filterConfig) {
this.filters.add(filterConfig);
}
创建调用链条代码,是在StandardWrapperValve invoke()方法中调用的
/**
* Construct and return a FilterChain implementation that will wrap the
* execution of the specified servlet instance. If we should not execute
* a filter chain at all, return <code>null</code>.
* <p>
* <strong>FIXME</strong> - Pool the chain instances!
*
* @param request The servlet request we are processing
* @param servlet The servlet instance to be wrapped
*/
private ApplicationFilterChain createFilterChain(Request request,
Servlet servlet) { // If there is no servlet to execute, return null
if (servlet == null)
return (null); // Create and initialize a filter chain object
ApplicationFilterChain filterChain =
new ApplicationFilterChain();
filterChain.setServlet(servlet);
StandardWrapper wrapper = (StandardWrapper) getContainer();
filterChain.setSupport(wrapper.getInstanceSupport()); // Acquire the filter mappings for this Context
StandardContext context = (StandardContext) wrapper.getParent();
FilterMap filterMaps[] = context.findFilterMaps(); // If there are no filter mappings, we are done
if ((filterMaps == null) || (filterMaps.length == 0))
return (filterChain);
// if (debug >= 1)
// log("createFilterChain: Processing " + filterMaps.length +
// " filter map entries"); // Acquire the information we will need to match filter mappings
String requestPath = null;
if (request instanceof HttpRequest) {
HttpServletRequest hreq =
(HttpServletRequest) request.getRequest();
String contextPath = hreq.getContextPath();
if (contextPath == null)
contextPath = "";
String requestURI = ((HttpRequest) request).getDecodedRequestURI();
if (requestURI.length() >= contextPath.length())
requestPath = requestURI.substring(contextPath.length());
}
String servletName = wrapper.getName();
// if (debug >= 1) {
// log(" requestPath=" + requestPath);
// log(" servletName=" + servletName);
// }
int n = 0; // Add the relevant path-mapped filters to this filter chain
for (int i = 0; i < filterMaps.length; i++) {
// if (debug >= 2)
// log(" Checking path-mapped filter '" +
// filterMaps[i] + "'");
if (!matchFiltersURL(filterMaps[i], requestPath))
continue;
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) //获取使用 filterConfig,这是上文创建的
context.findFilterConfig(filterMaps[i].getFilterName());
if (filterConfig == null) {
// if (debug >= 2)
// log(" Missing path-mapped filter '" +
// filterMaps[i] + "'");
; // FIXME - log configuration problem
continue;
}
// if (debug >= 2)
// log(" Adding path-mapped filter '" +
// filterConfig.getFilterName() + "'");
filterChain.addFilter(filterConfig); //
n++;
} // Add filters that match on servlet name second
for (int i = 0; i < filterMaps.length; i++) {
// if (debug >= 2)
// log(" Checking servlet-mapped filter '" +
// filterMaps[i] + "'");
if (!matchFiltersServlet(filterMaps[i], servletName))
continue;
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
context.findFilterConfig(filterMaps[i].getFilterName());
if (filterConfig == null) {
// if (debug >= 2)
// log(" Missing servlet-mapped filter '" +
// filterMaps[i] + "'");
; // FIXME - log configuration problem
continue;
}
// if (debug >= 2)
// log(" Adding servlet-mapped filter '" +
// filterMaps[i] + "'");
filterChain.addFilter(filterConfig);
n++;
} // Return the completed filter chain
// if (debug >= 2)
// log(" Returning chain with " + n + " filters");
return (filterChain); }
所以
void addFilter(ApplicationFilterConfig filterConfig) {
this.filters.add(filterConfig);
}
里面保存的不是filter ,是filterConfig, 可以理解为是个filter的工厂方法,调用getfilter() 能得到filter对象,上述就完成了第一问题,那么如果比配的呢?
答案是这个样子的
StandardWrapperValve类
调用 ===>createFilterChain
====> ApplicationFilterChain filterChain = new ApplicationFilterChain();
====>
if (!matchFiltersURL(filterMaps[i], requestPath))
continue;
filterChain.addFilter(filterConfig);
就是说,每一个invoke 都会创建一个过滤链,每次的过滤连添加的对象根据filtermap的规则进行匹配,如果匹配,给给这个调用链添加filter 否则不添加
所以匹配规则为
private boolean matchFiltersURL(FilterMap filterMap,
String requestPath) { // if (debug >= 3)
// log(" Matching request path '" + requestPath +
// "' against mapping " + filterMap); if (requestPath == null)
return (false); // Match on context relative request path
String testPath = filterMap.getURLPattern();
if (testPath == null)
return (false); // Case 1 - Exact Match
if (testPath.equals(requestPath))
return (true); // Case 2 - Path Match ("/.../*")
if (testPath.equals("/*"))
return (true); // Optimize a common case
if (testPath.endsWith("/*")) {
String comparePath = requestPath;
while (true) {
if (testPath.equals(comparePath + "/*"))
return (true);
int slash = comparePath.lastIndexOf('/');
if (slash < 0)
break;
comparePath = comparePath.substring(0, slash);
}
return (false);
} // Case 3 - Extension Match
if (testPath.startsWith("*.")) {
int slash = requestPath.lastIndexOf('/');
int period = requestPath.lastIndexOf('.');
if ((slash >= 0) && (period > slash))
return (testPath.equals("*." +
requestPath.substring(period + 1)));
} // Case 4 - "Default" Match
return (false); // NOTE - Not relevant for selecting filters }
看了源码就知道了规则为
String testPath = filterMap.getURLPattern();
- exact match 完全相符 testPath.equals(requestPath)
Case 2 - Path Match ("/.../*")if (testPath.equals("/*"))if (testPath.endsWith("/*"))
Case 3 - Extension Match
if (testPath.startsWith("*."))
规则很简单,1是完全匹配,路径对路径,一字不差; 2是以/*结尾的;3是包含*.的
深入刨析tomcat 之---第8篇 how tomcat works 第11章 11.9应用程序,自定义Filter,及注册的更多相关文章
- 深入刨析tomcat 之---第9篇 how tomcat works 第9章,Session的实现 关于request.setContext(context)
writedby 张艳涛,在学第9章session的时候,做了个实验在给的demo代码中添加了 package com.zyt.tomcat.ex09.core; public class Simpl ...
- 深入刨析tomcat 之---第12篇 how tomcat works( 第17章 ) 解析catalina.bat 梳理启动流程
我们如何启动tomcat呢? 答案是双击startup.bat文件,这个文件在bin目录下 @echo off 不显示批处理命令 rem Licensed to the Apache Softw ...
- 深入刨析tomcat 之---第10篇 how tomcat works 第13章,Response 发送错误信息 sendError
writedby 张艳涛 在浏览器中发送一个错误应用url 那么tomcat是如何发送错误的呢? 基本上是发送http 的response协议,分为两部分一部分是response设置头信息, 那么先分 ...
- 深入刨析tomcat 之---第6篇 how tomcat works 第5章 容器实现原理
writedby 张艳涛
- 深入刨析tomcat 之---第2篇,解决第3章bug 页面不显示内容http://localhost:8080/servlet/ModernServlet?userName=zhangyantao&password=1234
writedby 张艳涛7月2日, 在学习第4张的过程中,发现了前一篇文章写的是关于1,2张的bug不用设置response响应头,需要在servlet的service()方法里面写是错误想 serv ...
- 深入刨析tomcat 之---第23篇 聊一下web容器的filter配置和defaultservet
writedby 张艳涛,在一个webapp应用程序内如何配置filter? <?xml version="1.0" encoding="ISO-8859-1&qu ...
- Orchard 刨析:导航篇
之前承诺过针对Orchard Framework写一个系列.本应该在昨天写下这篇导航篇,不过昨天比较累偷懒的去玩了两盘单机游戏哈哈.下面进入正题. 写在前面 面向读者 之前和本文一再以Orchard ...
- 30s源码刨析系列之函数篇
前言 由浅入深.逐个击破 30SecondsOfCode 中函数系列所有源码片段,带你领略源码之美. 本系列是对名库 30SecondsOfCode 的深入刨析. 本篇是其中的函数篇,可以在极短的时间 ...
- Orchard 刨析:Logging
最近事情比较多,有预研的,有目前正在研发的,都是很需要时间的工作,所以导致这周只写了两篇Orchard系列的文章,这边不能保证后期会很频繁的更新该系列,但我会写完这整个系列,包括后面会把正在研发的东西 ...
随机推荐
- centos 7查看系统网络情况netstat
查看系统网络情况 netstat ➢ 基本语法 netstat [选项] ➢ 选项说明 -an 按一定顺序排列输出 -p 显示哪个进程在调用 应用案例 请查看服务名为 sshd 的服务的信息. ➢ N ...
- noip2015 总结
神奇的幻方 题目描述 幻方是一种很神奇的N*N矩阵:它由数字1,2,3,--,N构成,且每行.每列及两条对角线上的数字之和都相同. 当N为奇数时,我们可以通过以下方法构建一个幻方:首先将1写在第一行的 ...
- springboot bean的循环依赖实现 源码分析
springboot bean的循环依赖实现 源码分析 本文基于springboot版本2.5.1 <parent> <groupId>org.springframework. ...
- 每日三道面试题,通往自由的道路4——JVM篇
茫茫人海千千万万,感谢这一秒你看到这里.希望我的面试题系列能对你的有所帮助!共勉! 愿你在未来的日子,保持热爱,奔赴山海! 每日三道面试题,成就更好自我 昨天既然你有讲到字符串常量池是吧,那这样吧 1 ...
- 【Azure 应用服务】Azure Function集成虚拟网络,设置被同在虚拟网络中的Storage Account触发,遇见Function无法触发的问题
一切为了安全,所有的云上资源如支持内网资源访问,则都可以加入虚拟网络 问题描述 使用Azure Function处理Storage Account中Blob 新增,更新,删除等情况.Storage A ...
- 一致性hash原理 看这一篇就够了
在了解一致性哈希算法之前,最好先了解一下缓存中的一个应用场景,了解了这个应用场景之后,再来理解一致性哈希算法,就容易多了,也更能体现出一致性哈希算法的优点,那么,我们先来描述一下这个经典的分布式缓 ...
- Linux下的SSH,你知道多少?
Openssh介绍 OpenSSH 是 SSH (Secure Shell) 协议的免费开源实现.SSH协议族可以用来进行远程控制, 或在计算机之间传送文件.而实现此功能的传统方式,如telnet(终 ...
- 企业实施CRM系统 创造更多利润 - Zoho CRM
对企业来说,客户关系是一种投资.我们都知道企业的资源是有限的,因此必须要将这些有限的资源投入到能够带来持续价值的客户身上.而只有良好的客户关系才能够提高客户的忠诚度,多次购买甚至溢价购买企业的产品,持 ...
- C# 8.0和.NET Core 3.0高级编程 分享笔记二:编程基础第一部分
基础部分被我分为了2篇,因为实在太多了,但是每一个知识点我都不舍得删除,所以越写越多,这一篇博客整理了4个夜晚,内容有点多建议慢慢看.本章涵盖以下主题: 介绍C# 理解C#的基础知识 使用变量 处理空 ...
- vue3 script setup 定稿
vue script setup 已经官宣定稿.本文主要翻译了来自 0040-script-setup 的内容. 摘要 在单文件组件(SFC)中引入一个新的 <script> 类型 set ...