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();
  1. exact match  完全相符 testPath.equals(requestPath)
  2. Case 2 - Path Match ("/.../*")
    1. if (testPath.equals("/*"))
    2. if (testPath.endsWith("/*"))
  3. Case 3 - Extension Match
      if (testPath.startsWith("*."))

规则很简单,1是完全匹配,路径对路径,一字不差; 2是以/*结尾的;3是包含*.的

深入刨析tomcat 之---第8篇 how tomcat works 第11章 11.9应用程序,自定义Filter,及注册的更多相关文章

  1. 深入刨析tomcat 之---第9篇 how tomcat works 第9章,Session的实现 关于request.setContext(context)

    writedby 张艳涛,在学第9章session的时候,做了个实验在给的demo代码中添加了 package com.zyt.tomcat.ex09.core; public class Simpl ...

  2. 深入刨析tomcat 之---第12篇 how tomcat works( 第17章 ) 解析catalina.bat 梳理启动流程

    我们如何启动tomcat呢? 答案是双击startup.bat文件,这个文件在bin目录下 @echo off    不显示批处理命令 rem Licensed to the Apache Softw ...

  3. 深入刨析tomcat 之---第10篇 how tomcat works 第13章,Response 发送错误信息 sendError

    writedby 张艳涛 在浏览器中发送一个错误应用url 那么tomcat是如何发送错误的呢? 基本上是发送http 的response协议,分为两部分一部分是response设置头信息, 那么先分 ...

  4. 深入刨析tomcat 之---第6篇 how tomcat works 第5章 容器实现原理

    writedby 张艳涛

  5. 深入刨析tomcat 之---第2篇,解决第3章bug 页面不显示内容http://localhost:8080/servlet/ModernServlet?userName=zhangyantao&password=1234

    writedby 张艳涛7月2日, 在学习第4张的过程中,发现了前一篇文章写的是关于1,2张的bug不用设置response响应头,需要在servlet的service()方法里面写是错误想 serv ...

  6. 深入刨析tomcat 之---第23篇 聊一下web容器的filter配置和defaultservet

    writedby 张艳涛,在一个webapp应用程序内如何配置filter? <?xml version="1.0" encoding="ISO-8859-1&qu ...

  7. Orchard 刨析:导航篇

    之前承诺过针对Orchard Framework写一个系列.本应该在昨天写下这篇导航篇,不过昨天比较累偷懒的去玩了两盘单机游戏哈哈.下面进入正题. 写在前面 面向读者 之前和本文一再以Orchard ...

  8. 30s源码刨析系列之函数篇

    前言 由浅入深.逐个击破 30SecondsOfCode 中函数系列所有源码片段,带你领略源码之美. 本系列是对名库 30SecondsOfCode 的深入刨析. 本篇是其中的函数篇,可以在极短的时间 ...

  9. Orchard 刨析:Logging

    最近事情比较多,有预研的,有目前正在研发的,都是很需要时间的工作,所以导致这周只写了两篇Orchard系列的文章,这边不能保证后期会很频繁的更新该系列,但我会写完这整个系列,包括后面会把正在研发的东西 ...

随机推荐

  1. vue3.0搭建项目

    安装node.js 查看版本node -v 安装vue3.0版本之前需要先把vue-cli升级到4.0版本, 如果之前安装过vue-cli需要把2.0相关的卸载之后重新安装,npm uni -g vu ...

  2. Luat Inside | 多功能YAP物联网终端机,你不会还不知道吧?

    简洁高效是合宙产品的一个重要特点,合宙的工程师们用Demo取代繁杂的说明书,以便于开发者快速上手. 有没有可能把这个学习的过程变得更有趣,并且把技术入门难度进一步降低?作为一名Luat技术爱好者,我对 ...

  3. 拦截导弹(CDQ分治,DP)

    很好的题,值得细细说,(果然又是个假期望)....... 首先我们提取信息,显然这是个三维偏序问题 用简单的DP式子表示需要满足 f[i]=max(f[1--j]+1)(v[j]<v[i],h[ ...

  4. matplotlib 并列条形图

    1 绘制并列条形图,并在条形图上加标注 1.1 代码 from matplotlib import pyplot from matplotlib import font_manager import ...

  5. Windows 11,一个新功能,一场新屠杀

    6月24日,微软正式公布了新一代操作系统:Windows 11.这次的更新距离上一代操作系统Windows 10的发布,隔了有6年之久. 在新一代的操作系统中,包含了这些亮点: 采用了全新的UI设计. ...

  6. zookeeper使用教程

    Zookeeper 1.       Zookeeper概述 1.1 概述 Zookeeper是一个开源的分布式的,为分布式应用提供协调服务的Apache项目. 1.2 特点 1)Zookeeper: ...

  7. split截取字符串

    一.根据单个分隔字符用split截取字符串:string st="GT123_1";split代码:string[] sArray=st.split("_"); ...

  8. 解决“与 Microsoft Exchange 的连接不可用,Outlook 必须联机或已连接才能完成此操作”

    Microsoft Outlook 是一种用于发送和接收电子邮件的应用程序.由于其可靠性和各种使用类型,它在企业公司中非常受欢迎.Outlook 还可用于管理各种类型的个人数据,如日历约会.联系人.邮 ...

  9. Docker 优雅终止方案

    作为一名系统工程师,你可能经常需要重启容器,毕竟Kubernetes的优势就是快速弹性伸缩和故障恢复,遇到问题先重启容器再说,几秒钟即可恢复,实在不行再重启系统,这就是系统重启工程师的杀手锏.然而现实 ...

  10. jenkins在aws eks中的CI/CD及slave

    本文档不讲解jenkins的基础用法,主要讲解配合k8s的插件,和pipeline的用途和k8s ci/cd的流程. 以及部署在k8s集群内和集群外的注意事项. 1.准备工作 以下在整个CI/CD流程 ...