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