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

时间:2022-06-01 07:17:37

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是包含*.的