深入刨析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是包含*.的