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

  • 相关阅读:
    如何解决DEDE织梦友情链接字数限制与链接个数限制的问题?
    织梦内容页调取文章缩略图
    如何删除织梦系统power by dedecms
    在联系我们里面添加公司地图坐标地理位置
    织梦后台添加了一篇文章 但是前台显示两篇一模一样的文章
    dedecms织梦上下页标签和CSS
    ZooKeeper+Dubbox分布式框架
    mac远程控制linux,安装jdk
    mac下myeclipse2017安装activiti插件
    java为什么要实现serializable序列化
  • 原文地址:https://www.cnblogs.com/zytcomeon/p/15002160.html
Copyright © 2020-2023  润新知