2019-10-28 17:45  阅读(1839)
文章分类:Tomcat 源码分析 文章标签:TomcatTomcat 源码
©  原文作者:clawhub 原文地址:http://clawhub.club

filter是Servlet的规范,是在请求进入容器后,执行Servlet.service方法之前执行。
Tomcat处理filter分为三步:

  • 上下文初始化时读取web.xml中的filter数据
  • 请求处理时,根据请求路径找到相应的过滤器
  • 执行过滤器链,调用doFilter方法与目标方法。

初始化filter数据

首先在ContextConfig初始化时,读取web.xml配置文件,将其中的filter信息封装为FilterDef存储到StandardContext:

        private void configureContext(WebXml webxml) {
        //略......
        for (FilterDef filter : webxml.getFilters().values()) {
            if (filter.getAsyncSupported() == null) {
                 filter.setAsyncSupported("false");
            }
            context.addFilterDef(filter);
        }
        for (FilterMap filterMap : webxml.getFilterMappings()) {
            context.addFilterMap(filterMap);
        }
        //略......
        }
    

之后在StandardContext初始化时调用filterStart,组装好filterConfigs供后期使用:

         /**
             * Configure and initialize the set of filters for this Context.
             * @return <code>true</code> if all filter initialization completed
             * successfully, or <code>false</code> otherwise.
             */
            public boolean filterStart() {
    
                if (getLogger().isDebugEnabled()) {
                    getLogger().debug("Starting filters");
                }
                // Instantiate and record a FilterConfig for each defined filter
                boolean ok = true;
                synchronized (filterConfigs) {
                    filterConfigs.clear();
                    for (Entry<String,FilterDef> entry : filterDefs.entrySet()) {
                        String name = entry.getKey();
                        if (getLogger().isDebugEnabled()) {
                            getLogger().debug(" Starting filter '" + name + "'");
                        }
                        try {
                            //这里会调用 Filter.init 方法
                            ApplicationFilterConfig filterConfig =
                                    new ApplicationFilterConfig(this, entry.getValue());
                            filterConfigs.put(name, filterConfig);
                        } catch (Throwable t) {
                            t = ExceptionUtils.unwrapInvocationTargetException(t);
                            ExceptionUtils.handleThrowable(t);
                            getLogger().error(sm.getString(
                                    "standardContext.filterStart", name), t);
                            ok = false;
                        }
                    }
                }
    
                return ok;
            }
    

创建过滤器链

创建过滤器链方法调用位于StandardWrapperValve的invoke中:

         //为这个请求创建过滤器链
        ApplicationFilterChain filterChain =
                ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
    

进入ApplicationFilterFactory中:

        /**
             * 构造一个FilterChain实现,它将封装指定servlet实例的执行。
             *
             * @param request 我们正在处理的servlet请求,javax.servlet.ServletRequest
             * @param wrapper 管理servlet实例的包装器  org.apache.catalina.Wrapper
             * @param servlet 要包装的servlet实例 javax.servlet.Servlet
             * @return 配置的FilterChain实例,如果不执行,则为null。
             */
            public static ApplicationFilterChain createFilterChain(ServletRequest request,
                                                                   Wrapper wrapper, Servlet servlet) {
    
                // 如果没有要执行的servlet,则返回null
                if (servlet == null)
                    return null;
    
                // 创建并初始化过滤器链对象
                ApplicationFilterChain filterChain = null;
                //这里的Request为org.apache.catalina.connector.Request
                if (request instanceof Request) {
                    Request req = (Request) request;
                    //SecurityManager是否打开
                    if (Globals.IS_SECURITY_ENABLED) {
                        // Security: Do not recycle 不回收
                        filterChain = new ApplicationFilterChain();
                    } else {
                        //从请求中获取ApplicationFilterChain,也就是回收的
                        filterChain = (ApplicationFilterChain) req.getFilterChain();
                        if (filterChain == null) {
                            filterChain = new ApplicationFilterChain();
                            req.setFilterChain(filterChain);
                        }
                    }
                } else {
                    // 正在使用的请求调度程序,ApplicationDispatcher调用过来的
                    filterChain = new ApplicationFilterChain();
                }
    
                //设置将在这个链的末尾执行的servlet。
                filterChain.setServlet(servlet);
                //关联的servlet实例是否支持异步处理?
                filterChain.setServletSupportsAsync(wrapper.isAsyncSupported());
    
                // 获取此上下文的过滤器映射
                StandardContext context = (StandardContext) wrapper.getParent();
                FilterMap filterMaps[] = context.findFilterMaps();
    
                // 如果没有过滤器映射,我们就完成了
                if ((filterMaps == null) || (filterMaps.length == 0))
                    return filterChain;
    
                // 获取匹配筛选器映射所需的信息
                DispatcherType dispatcher =
                        (DispatcherType) request.getAttribute(Globals.DISPATCHER_TYPE_ATTR);
    
                String requestPath = null;
                Object attribute = request.getAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR);
                if (attribute != null) {
                    requestPath = attribute.toString();
                }
    
                //返回描述此容器的名称字符串
                String servletName = wrapper.getName();
    
                //将相关的路径映射过滤器添加到此过滤器链
                for (int i = 0; i < filterMaps.length; i++) {
                    //dispatcher类型与FilterMap中指定的dispatcher类型是否匹配
                    if (!matchDispatcher(filterMaps[i], dispatcher)) {
                        continue;
                    }
                    //上下文相关的请求路径符合指定的筛选器映射的要求
                    if (!matchFiltersURL(filterMaps[i], requestPath))
                        continue;
                    //从上下文context中获取ApplicationFilterConfig
                    ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
                            context.findFilterConfig(filterMaps[i].getFilterName());
                    if (filterConfig == null) {
                        // FIXME - log configuration problem
                        continue;
                    }
                    //将过滤器添加到将在此链中执行的过滤器集中。
                    filterChain.addFilter(filterConfig);
                }
    
                // 添加与servlet名称第二次匹配的过滤器
                for (int i = 0; i < filterMaps.length; i++) {
                    //dispatcher类型与FilterMap中指定的dispatcher类型是否匹配
                    if (!matchDispatcher(filterMaps[i], dispatcher)) {
                        continue;
                    }
                    //如果指定的servlet名称符合指定的筛选器映射的要求
                    if (!matchFiltersServlet(filterMaps[i], servletName))
                        continue;
                    //从上下文context中获取ApplicationFilterConfig
                    ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
                            context.findFilterConfig(filterMaps[i].getFilterName());
                    if (filterConfig == null) {
                        // FIXME - log configuration problem
                        continue;
                    }
                    //将过滤器添加到将在此链中执行的过滤器集中。
                    filterChain.addFilter(filterConfig);
                }
    

最终过滤器加入了ApplicationFilterConfig数组的末尾。

执行过滤器链

        filterChain.doFilter(request.getRequest(), response.getResponse());
    

最终会调用到ApplicationFilterChain的internalDoFilter方法:

         private void internalDoFilter(ServletRequest request, ServletResponse response)
                    throws IOException, ServletException {
    
                // 如果有一个过滤器,调用下一个过滤器
                //pos:过滤器链中当前位置的索引。
                //n:链中过滤器当前数量
                if (pos < n) {
                    //获取过滤器配置
                    ApplicationFilterConfig filterConfig = filters[pos++];
                    try {
                        //获取过滤器,会调用:filter.init
                        Filter filter = filterConfig.getFilter();
    
                        //异步处理
                        if (request.isAsyncSupported() && "false".equalsIgnoreCase(
                                filterConfig.getFilterDef().getAsyncSupported())) {
                            request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE);
                        }
                        //调用doFilter,处理完过滤逻辑之后还会继续调用当前过滤器链的doFilter方法
                        if (Globals.IS_SECURITY_ENABLED) {
                            final ServletRequest req = request;
                            final ServletResponse res = response;
                            Principal principal =
                                    ((HttpServletRequest) req).getUserPrincipal();
    
                            Object[] args = new Object[]{req, res, this};
                            SecurityUtil.doAsPrivilege("doFilter", filter, classType, args, principal);
                        } else {
                            filter.doFilter(request, response, this);
                        }
                    } catch (IOException | ServletException | RuntimeException e) {
                        throw e;
                    } catch (Throwable e) {
                        e = ExceptionUtils.unwrapInvocationTargetException(e);
                        ExceptionUtils.handleThrowable(e);
                        throw new ServletException(sm.getString("filterChain.filter"), e);
                    }
                    return;
                }
    
                // We fell off the end of the chain -- call the servlet instance
                //准备调用servlet.service方法
                try {
                    if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
                        lastServicedRequest.set(request);
                        lastServicedResponse.set(response);
                    }
    
                    if (request.isAsyncSupported() && !servletSupportsAsync) {
                        request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,
                                Boolean.FALSE);
                    }
                    // Use potentially wrapped request from this point
                    if ((request instanceof HttpServletRequest) &&
                            (response instanceof HttpServletResponse) &&
                            Globals.IS_SECURITY_ENABLED) {
                        final ServletRequest req = request;
                        final ServletResponse res = response;
                        Principal principal =
                                ((HttpServletRequest) req).getUserPrincipal();
                        Object[] args = new Object[]{req, res};
                        SecurityUtil.doAsPrivilege("service",
                                servlet,
                                classTypeUsedInService,
                                args,
                                principal);
                    } else {
                        //执行业务逻辑
                        servlet.service(request, response);
                    }
                } catch (IOException | ServletException | RuntimeException e) {
                    throw e;
                } catch (Throwable e) {
                    e = ExceptionUtils.unwrapInvocationTargetException(e);
                    ExceptionUtils.handleThrowable(e);
                    throw new ServletException(sm.getString("filterChain.servlet"), e);
                } finally {
                    if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
                        lastServicedRequest.set(null);
                        lastServicedResponse.set(null);
                    }
                }
            }
    

其中filter.doFilter,处理完过滤逻辑之后还会继续调用当前过滤器链的doFilter方法,直到所有过滤器链处理完毕。
最后执行Servlet的service方法,处理业务逻辑。

基本上将调用链的逻辑分析完了,下一篇简单的总结一下整个请求处理过程。


来源:https://www.jianshu.com/u/9632919f32c3

点赞(0)
版权归原创作者所有,任何形式转载请联系作者; Java 技术驿站 >> Tomcat源码分析【十六】请求处理过程分析之创建与执行过滤器链
上一篇
Tomcat源码分析【十五】请求处理过程分析之分配Servlet实例处理请求
下一篇
Tomcat源码分析【十七】请求处理过程分析之简单总结