tomcat源码—redirect和forward的实现

网上已经有很多关于redirect和forward区别的文章,更多的都是只是一些概念上的描述,虽然在大多情况下,知道这些就已经足够了。但也有例外:forward not working for struts2,why?我也是在工作中碰到了这个问题,才特意看了下tomcat有关这部分的源代码。深刻的了解下也无妨。 
redirect和forward都是属于servlet规范的,不同的servlet容器的实现可能会有一些区别,但原理都是类似的。 

redirect和forward的定义: 
1. redirect(重定向):服务端发送给客户端一个重定向的临时响应头,这个响应头包含重定向之后的URL,客户端用新的URL重新向服务器发送一个请求。 
2. forward(请求转向):服务器程序内部请求转向,这个特性允许前一个程序用于处理请求,而后一个程序用来返回响应。 

Redirect的原理比较简单,它的定义也已经描述的很清楚了,我也不想多讲什么,就贴一段简单的代码吧! 

org.apache.catalina.connector.Response#sendRedirect(String): 
  

Java代码    收藏代码
  1.  public void sendRedirect(String location)   
  2.         throws IOException {  
  3.   
  4.         if (isCommitted())  
  5.             throw new IllegalStateException  
  6.                 (sm.getString("coyoteResponse.sendRedirect.ise"));  
  7.   
  8.         // Ignore any call from an included servlet  
  9.         if (included)  
  10.             return;   
  11.   
  12.         // Clear any data content that has been buffered  
  13.         resetBuffer();  
  14.   
  15.         // Generate a temporary redirect to the specified location  
  16.         try {  
  17.             String absolute = toAbsolute(location);  
  18.             setStatus(SC_FOUND);  
  19.             setHeader("Location", absolute);  
  20.         } catch (IllegalArgumentException e) {  
  21.             setStatus(SC_NOT_FOUND);  
  22.         }  
  23.   
  24.         // Cause the response to be finished (from the application perspective)  
  25.         setSuspended(true);  
  26. }  


方法行为:先把相对路径转换成绝对路径,再包装一个包含有新的URL的临时响应头,“SC_FOUND”的值是302,就是重定向临时响应头的状态码。如果传入的“location”值不合法,就包装一个404的响应头。 

下面就来看看tomcat是如何实现forward的,forward为什么在struts2下会无效(注解:其实是可以设置的)。 

先看下程序是如何调用forward的: 

Java代码    收藏代码
  1. req.getRequestDispatcher("testForward").forward(req, resp);  


整个过程分两个步骤来执行 
1. 得到一个请求调度器 
2. 通过调度器把请求转发过去。 


第一步骤,获取请求调度器。 
org.apache.catalina.connector.Request#getRequestDispatcher(String) 

Java代码    收藏代码
  1.       
  2. public RequestDispatcher getRequestDispatcher(String path) {  
  3.   
  4.         if (request == null) {  
  5.             throw new IllegalStateException(  
  6.                             sm.getString("requestFacade.nullRequest"));  
  7.         }  
  8.   
  9.         if (Globals.IS_SECURITY_ENABLED){  
  10.             return (RequestDispatcher)AccessController.doPrivileged(  
  11.                 new GetRequestDispatcherPrivilegedAction(path));  
  12.         } else {  
  13.              return request.getRequestDispatcher(path);    
  14.         }  


方法行为:把获取RequestDispatcher的任务交个内部的request。它们之间的关系如下所示 

 


org.apache.catalina.connector.RequestFacade和类org.apache.catalina.connector.Request都是实现了javax.servlet.http.HttpServletRequest接口,而RequestFacade内部有包装了个Request,对Request的访问做了些控制,应该是代理模式 

org.apache.catalina.connector.Request#getRequestDispatcher(String) 

Java代码    收藏代码
  1. public RequestDispatcher getRequestDispatcher(String path) {  
  2.          if (path.startsWith("/"))  
  3.            return (context.getServletContext().getRequestDispatcher(path));  
  4.   
  5.        //省略了部分代码  
  6.        return (context.getServletContext().getRequestDispatcher(relative));   
  7.   
  8.    }  


方法行为:把绝对路径转换成相对路径,最终的格式如“/testForward”。若已经是这种格式的相对路径,就无需再转换了。 
接下来就转交给ServletContext来处理,ServletContext是web项目的一个上下文,包含所有的Servlet集合,还定义了一些Servlet与容器之间交互的接口。 
org.apache.catalina.core.ApplicationContext#getRequestDispatcher(String) 

Java代码    收藏代码
  1. public RequestDispatcher getRequestDispatcher(String path) {  
  2.           //省去部分代码  
  3.           context.getMapper().map(uriMB, mappingData);  
  4.           //省去部分代码  
  5.       Wrapper wrapper = (Wrapper) mappingData.wrapper;  
  6.       String wrapperPath = mappingData.wrapperPath.toString();  
  7.       String pathInfo = mappingData.pathInfo.toString();  
  8.   
  9.       mappingData.recycle();  
  10.         
  11.       // Construct a RequestDispatcher to process this request  
  12.       return new ApplicationDispatcher  
  13.           (wrapper, uriCC.toString(), wrapperPath, pathInfo,   
  14.            queryString, null);   
  15.   }  


方法行为:根据路径名“path”找到一个包含有Servlet的Wrapper,最后实例化一个ApplicationDispatcher,并且返回该ApplicationDispatcher。 

该方法里非常关键的一行:context.getMapper().map(uriMB, mappingData)。 
Mapper的类定义我不知道如何描述,就贴上原文吧:Mapper, which implements the servlet API mapping rules (which are derived from the HTTP rules)。 
不过只想了解forward的原理,熟悉map函数就够了。 

org.apache.tomcat.util.http.mapper.Mapper#map(org.apache.tomcat.util.buf.MessageBytes, org.apache.tomcat.util.http.mapper.MappingData): 

Java代码    收藏代码
  1. public void map(MessageBytes uri, MappingData mappingData)  
  2.     throws Exception {  
  3.   
  4.     uri.toChars();  
  5.     CharChunk uricc = uri.getCharChunk();  
  6.     uricc.setLimit(-1);  
  7.     internalMapWrapper(context, uricc, mappingData);  
  8.   
  9. }  


方法行为:。。。。。。。就介绍下参数吧,uri可以理解是path(“/testforward”)的一个变形,而mappingData用于存储当前线程用到的部分数据。该函数是没有返回值的,处理之后的结果就是存放到mappingData里的。 

org.apache.tomcat.util.http.mapper.Mapper#internalMapWrapper(Mapper$Context,org.apache.tomcat.util.buf.CharChunk, org.apache.tomcat.util.http.mapper.MappingData): 

Java代码    收藏代码
  1. private final void internalMapWrapper(Context context, CharChunk path,  
  2.                                          MappingData mappingData)  
  3.        throws Exception {  
  4.   
  5.        int pathOffset = path.getOffset();  
  6.        int pathEnd = path.getEnd();  
  7.        int servletPath = pathOffset;  
  8.        boolean noServletPath = false;  
  9.   
  10.        int length = context.name.length();  
  11.        if (length != (pathEnd - pathOffset)) {  
  12.            servletPath = pathOffset + length;  
  13.        } else {  
  14.            noServletPath = true;  
  15.            path.append('/');  
  16.            pathOffset = path.getOffset();  
  17.            pathEnd = path.getEnd();  
  18.            servletPath = pathOffset+length;  
  19.        }  
  20.   
  21.        path.setOffset(servletPath);  
  22.   
  23.        // Rule 1 -- Exact Match  
  24.        Wrapper[] exactWrappers = context.exactWrappers;  
  25.        internalMapExactWrapper(exactWrappers, path, mappingData);  
  26.   
  27.        // Rule 2 -- Prefix Match  
  28.        boolean checkJspWelcomeFiles = false;  
  29.        Wrapper[] wildcardWrappers = context.wildcardWrappers;  
  30.        if (mappingData.wrapper == null) {  
  31.            internalMapWildcardWrapper(wildcardWrappers, context.nesting,   
  32.                                       path, mappingData);  
  33.            if (mappingData.wrapper != null && mappingData.jspWildCard) {  
  34.                char[] buf = path.getBuffer();  
  35.                if (buf[pathEnd - 1] == '/') {  
  36.                    /* 
  37.                     * Path ending in '/' was mapped to JSP servlet based on 
  38.                     * wildcard match (e.g., as specified in url-pattern of a 
  39.                     * jsp-property-group. 
  40.                     * Force the context's welcome files, which are interpreted 
  41.                     * as JSP files (since they match the url-pattern), to be 
  42.                     * considered. See Bugzilla 27664. 
  43.                     */   
  44.                    mappingData.wrapper = null;  
  45.                    checkJspWelcomeFiles = true;  
  46.                } else {  
  47.                    // See Bugzilla 27704  
  48.                    mappingData.wrapperPath.setChars(buf, path.getStart(),  
  49.                                                     path.getLength());  
  50.                    mappingData.pathInfo.recycle();  
  51.                }  
  52.            }  
  53.        }  
  54.   
  55.        if(mappingData.wrapper == null && noServletPath) {  
  56.            // The path is empty, redirect to "/"  
  57.            mappingData.redirectPath.setChars  
  58.                (path.getBuffer(), pathOffset, pathEnd);  
  59.            path.setEnd(pathEnd - 1);  
  60.            return;  
  61.        }  
  62.   
  63.        // Rule 3 -- Extension Match  
  64.        Wrapper[] extensionWrappers = context.extensionWrappers;  
  65.        if (mappingData.wrapper == null && !checkJspWelcomeFiles) {  
  66.            internalMapExtensionWrapper(extensionWrappers, path, mappingData);  
  67.        }  
  68.   
  69.        // Rule 4 -- Welcome resources processing for servlets  
  70.        if (mappingData.wrapper == null) {  
  71.            boolean checkWelcomeFiles = checkJspWelcomeFiles;  
  72.            if (!checkWelcomeFiles) {  
  73.                char[] buf = path.getBuffer();  
  74.                checkWelcomeFiles = (buf[pathEnd - 1] == '/');  
  75.            }  
  76.            if (checkWelcomeFiles) {  
  77.                for (int i = 0; (i < context.welcomeResources.length)  
  78.                         && (mappingData.wrapper == null); i++) {  
  79.                    path.setOffset(pathOffset);  
  80.                    path.setEnd(pathEnd);  
  81.                    path.append(context.welcomeResources[i], 0,  
  82.                                context.welcomeResources[i].length());  
  83.                    path.setOffset(servletPath);  
  84.   
  85.                    // Rule 4a -- Welcome resources processing for exact macth  
  86.                    internalMapExactWrapper(exactWrappers, path, mappingData);  
  87.   
  88.                    // Rule 4b -- Welcome resources processing for prefix match  
  89.                    if (mappingData.wrapper == null) {  
  90.                        internalMapWildcardWrapper  
  91.                            (wildcardWrappers, context.nesting,   
  92.                             path, mappingData);  
  93.                    }  
  94.   
  95.                    // Rule 4c -- Welcome resources processing  
  96.                    //            for physical folder  
  97.                    if (mappingData.wrapper == null  
  98.                        && context.resources != null) {  
  99.                        Object file = null;  
  100.                        String pathStr = path.toString();  
  101.                        try {  
  102.                            file = context.resources.lookup(pathStr);  
  103.                        } catch(NamingException nex) {  
  104.                            // Swallow not found, since this is normal  
  105.                        }  
  106.                        if (file != null && !(file instanceof DirContext) ) {  
  107.                            internalMapExtensionWrapper(extensionWrappers,  
  108.                                                        path, mappingData);  
  109.                            if (mappingData.wrapper == null  
  110.                                && context.defaultWrapper != null) {  
  111.                                mappingData.wrapper =  
  112.                                    context.defaultWrapper.object;  
  113.                                mappingData.requestPath.setChars  
  114.                                    (path.getBuffer(), path.getStart(),   
  115.                                     path.getLength());  
  116.                                mappingData.wrapperPath.setChars  
  117.                                    (path.getBuffer(), path.getStart(),   
  118.                                     path.getLength());  
  119.                                mappingData.requestPath.setString(pathStr);  
  120.                                mappingData.wrapperPath.setString(pathStr);  
  121.                            }  
  122.                        }  
  123.                    }  
  124.                }  
  125.   
  126.                path.setOffset(servletPath);  
  127.                path.setEnd(pathEnd);  
  128.            }  
  129.                                          
  130.        }  


方法行为:通过“path”从“context”里找到对应的Servlet,存放到“mappingData”里。 
可以看到这里有7个匹配Servlet规则: 
1. Rule 1 -- Exact Match:精确匹配,匹配web.xml配置的格式如“<url-pattern>/testQiu</url-pattern>”的Servlet 
2. Rule 2 -- Prefix Matcha:前缀匹配,匹配的Servlet格式如“<url-pattern>/testQiu/*</url-pattern>” 
3. Rule 3 -- Extension Match:扩展匹配,匹配jsp或者jspx 
4. ---Rule 4a -- Welcome resources processing for exact macth: 
5. ---Rule 4b -- Welcome resources processing for prefix match: 
6. ---Rule 4c -- Welcome resources processing for physical folder: 
7. Rule 7 --如果前面6条都没匹配到,那就返回org.apache.catalina.servlets.DefaultServlet。 

其实这里真正的匹配的是Wapper,而不是Servlet,因为Wapper最重要的一个属性就是Servlet,说成“匹配Servlet”是为了更容易的表达。 

至此返回RequestDispatcher就结束了。 



接下来就是讲解RequestDispatcher.forward了。Forward的就不贴出全部的源代码,只贴一些重要的片段,绝大部分的逻辑都在org.apache.catalina.core.ApplicationDispatcher类里。 
先描述下过程: 
1. 设置request里的部分属性值,如:请求的路径、参数等。 
2. 组装一个FilterChain链,调用doFilter方法。 
3. 最后根据实际情况调用Filter的doFilter函数或者Servlet的service函数。 

注:FilterChain和Filter是两个不同的接口,两个接口的UML 

 

org.apache.catalina.core.ApplicationDispatcher#doForward(ServletRequest,ServletResponse): 

Java代码    收藏代码
  1. private void doForward(ServletRequest request, ServletResponse response)  
  2.         throws ServletException, IOException  
  3.          //省略了部分代码  
  4.         // Handle an HTTP named dispatcher forward  
  5.         if ((servletPath == null) && (pathInfo == null)) {  
  6.   ApplicationHttpRequest wrequest =
                        (ApplicationHttpRequest) wrapRequest(state);
                    HttpServletRequest hrequest = state.hrequest;
                    wrequest.setRequestURI(hrequest.getRequestURI());
                    wrequest.setContextPath(hrequest.getContextPath());
                       wrequest.setServletPath(hrequest.getServletPath());
                    wrequest.setPathInfo(hrequest.getPathInfo());
                    wrequest.setQueryString(hrequest.getQueryString());
                processRequest(request,response,state);
  7. //省略了部分代码  
  8.         } else {// Handle an HTTP path-based forward  
  9.             ApplicationHttpRequest wrequest =  
  10.                 (ApplicationHttpRequest) wrapRequest(state);  
  11.             String contextPath = context.getPath();  
  12.             HttpServletRequest hrequest = state.hrequest;  
  13.             if (hrequest.getAttribute(Globals.FORWARD_REQUEST_URI_ATTR) == null) {  
  14.                 wrequest.setAttribute(Globals.FORWARD_REQUEST_URI_ATTR,  
  15.                                       hrequest.getRequestURI());  
  16.                 wrequest.setAttribute(Globals.FORWARD_CONTEXT_PATH_ATTR,  
  17.                                       hrequest.getContextPath());  
  18.                 wrequest.setAttribute(Globals.FORWARD_SERVLET_PATH_ATTR,  
  19.                                       hrequest.getServletPath());  
  20.                 wrequest.setAttribute(Globals.FORWARD_PATH_INFO_ATTR,  
  21.                                       hrequest.getPathInfo());  
  22.                 wrequest.setAttribute(Globals.FORWARD_QUERY_STRING_ATTR,  
  23.                                       hrequest.getQueryString());  
  24.             }  
  25.    
  26.             wrequest.setContextPath(contextPath);  
  27.             wrequest.setRequestURI(requestURI);  
  28.             wrequest.setServletPath(servletPath);  
  29.             wrequest.setPathInfo(pathInfo);  
  30.             if (queryString != null) {  
  31.                 wrequest.setQueryString(queryString);  
  32.                 wrequest.setQueryParams(queryString);  
  33.             }  
  34.   
  35.             processRequest(request,response,state);  
  36.         }  
  37.         }  
  38.    
  39.           if (wrapper.getLogger().isDebugEnabled() )
                    wrapper.getLogger().debug(" Disabling the response for futher output");
     
                     if  (response instanceof ResponseFacade) {
                    ((ResponseFacade) response).finish();
                 } else {
                    // Servlet SRV.6.2.2. The Request/Response may have been wrapped
                    // and may no longer be instance of RequestFacade
                     。。。
        
                    // Close anyway
                    try {
                        PrintWriter writer = response.getWriter();
                        writer.close();
                } catch (IllegalStateException e) {
                。。
                    }
                }

  40.     }  

 

 

   private ServletRequest wrapRequest(State state) {
	        // Locate the request we should insert in front of
	        ServletRequest previous = null;
	        ServletRequest current = state.outerRequest;
                ServletRequest wrapper = null;
                     //省略
	        if ((current instanceof ApplicationHttpRequest) ||
	            (current instanceof Request) ||
	            (current instanceof HttpServletRequest)) {
		    //省略
                    wrapper = new ApplicationHttpRequest(hcurrent, context, crossContext);
        } else {
	            wrapper = new ApplicationRequest(current);
	        }
         }


  下面是 ApplicationRequest类class ApplicationRequest extends ServletRequestWrapper
 public ApplicationRequest(ServletRequest request) { 
         super(request);
	 setRequest(request);
	    }

 

 

 

private void processRequest(ServletRequest request,
	                                ServletResponse response,
	                                State state)
	        throws IOException, ServletException {
	                 
	        DispatcherType disInt = (DispatcherType) request.getAttribute(ApplicationFilterFactory.DISPATCHER_TYPE_ATTR);
	        if (disInt != null) {
	         ....
	                invoke(state.outerRequest, response, state);
	            } else {
	                invoke(state.outerRequest, response, state);
	            }
	        }
	    }

 

 private void invoke(ServletRequest request, ServletResponse response,
	            State state) throws IOException, ServletException {
	 
	        // Checking to see if the context classloader is the current context
	        // classloader. If it's not, we're saving it, and setting the context
	        // classloader to the Context classloader
	        ClassLoader oldCCL = Thread.currentThread().getContextClassLoader();
	        ClassLoader contextClassLoader = context.getLoader().getClassLoader();
	 
	        if (oldCCL != contextClassLoader) {
	            Thread.currentThread().setContextClassLoader(contextClassLoader);
	        } else {
	            oldCCL = null;
	        }
	 
	        // Initialize local variables we may need
	        HttpServletResponse hresponse = state.hresponse;
	        Servlet servlet = null;
	        IOException ioException = null;
	        ServletException servletException = null;
	        RuntimeException runtimeException = null;
	        boolean unavailable = false;
	 
	        // Check for the servlet being marked unavailable
	        if (wrapper.isUnavailable()) {
	            wrapper.getLogger().warn(
	                    sm.getString("applicationDispatcher.isUnavailable",
	                    wrapper.getName()));
	            long available = wrapper.getAvailable();
	            if ((available > 0L) && (available < Long.MAX_VALUE))
	                hresponse.setDateHeader("Retry-After", available);
	            hresponse.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, sm
	                    .getString("applicationDispatcher.isUnavailable", wrapper
	                            .getName()));
	            unavailable = true;
	        }
	 
	        // Allocate a servlet instance to process this request
	        try {
	            if (!unavailable) {
	                servlet = wrapper.allocate();
	            }
	        } catch (ServletException e) {
	            wrapper.getLogger().error(sm.getString("applicationDispatcher.allocateException",
	                             wrapper.getName()), StandardWrapper.getRootCause(e));
	            servletException = e;
	        } catch (Throwable e) {
	            ExceptionUtils.handleThrowable(e);
	            wrapper.getLogger().error(sm.getString("applicationDispatcher.allocateException",
	                             wrapper.getName()), e);
	            servletException = new ServletException
	                (sm.getString("applicationDispatcher.allocateException",
	                              wrapper.getName()), e);
	            servlet = null;
	        }
	                 
	        // Get the FilterChain Here
	        ApplicationFilterFactory factory = ApplicationFilterFactory.getInstance();
	        ApplicationFilterChain filterChain = factory.createFilterChain(request, wrapper,servlet);
	         
	        // Call the service() method for the allocated servlet instance
	        try {
	            support.fireInstanceEvent(InstanceEvent.BEFORE_DISPATCH_EVENT,
	                                      servlet, request, response);
	            // for includes/forwards
	            if ((servlet != null) && (filterChain != null)) {
	               filterChain.doFilter(request, response);
	             }
	            // Servlet Service Method is called by the FilterChain
	            support.fireInstanceEvent(InstanceEvent.AFTER_DISPATCH_EVENT,
	                                      servlet, request, response);
	        } catch (ClientAbortException e) {
                       。。。
	        }
	        // Release the filter chain (if any) for this request
	        try {
	            if (filterChain != null)
	                filterChain.release();
	        } catch (Throwable e) {
	           。。
               }
	            // FIXME: Exception handling needs to be similar to what is in the StandardWrapperValue
	 
	        // Deallocate the allocated servlet instance
	        try {
	            if (servlet != null) {
	                wrapper.deallocate(servlet);
	            }
	        } catch (ServletException e) {
	           。。
	        }
	 
        // Reset the old context class loader
	        if (oldCCL != null)
            Thread.currentThread().setContextClassLoader(oldCCL);
         
        // Unwrap request/response if needed
        // See Bugzilla 30949
	        unwrapRequest(state);
	        unwrapResponse(state);
	        // Recycle request if necessary (also BZ 30949)
        recycleRequestWrapper(state);
	         
	        // Rethrow an exception if one was thrown by the invoked servlet
	        if (ioException != null)
	            throw ioException;
	        if (servletException != null)
	            throw servletException;
	        if (runtimeException != null)
	            throw runtimeException;
    }
	 

 


第1步:设置新的request的属性: 
         

Java代码    收藏代码
  1. wrequest.setContextPath(contextPath);  
  2.           wrequest.setRequestURI(requestURI);  
  3.           wrequest.setServletPath(servletPath);  
  4.           wrequest.setPathInfo(pathInfo);  
  5.           if (queryString != null) {  
  6.               wrequest.setQueryString(queryString);  
  7.               wrequest.setQueryParams(queryString);  
  8.           }  

第2步: 分配一个Servelt实例 

 

                   servlet = wrapper.allocate();                 
                  // 如果 用户请求的是用户自己写的Servlet,就会返回直接由开发人员自己编写的servlet实例,
                  //如果请求是一个.JSP文件时,Servlet是org.apache.jasper.servlet.JspServlet的实例,
                 // 如果请求是一个静态资源时,Servlet用的是org.apache.catalina.servlets.DefaultServlet实例。
                    servlet = wrapper.allocate(); 
                    //在获取这个servlet实例时都调用了 servlet.init()方法了对资源进行初始化。


第3步:组装FitlerChain链,根据web.xml配置信息,是否决定添加Filter---- 
<filter-mapping> 
<filter-name>struts2</filter-name> 
<url-pattern>/*</url-pattern> 
<dispatcher>REQUEST</dispatcher> 
</filter-mapping> 


org.apache.catalina.core.ApplicationFilterFactory#createFilterChain(ServletRequest, Wrapper, Servlet):
 

Java代码    收藏代码
  1. public ApplicationFilterChain createFilterChain(ServletRequest request, Wrapper wrapper, Servlet servlet) {  
  2.         //省略部分代码  
  3.             filterChain = new ApplicationFilterChain();  
  4.         }  
  5.   
  6.         filterChain.setServlet(servlet);  
  7.   
  8.         filterChain.setSupport  
  9.             (((StandardWrapper)wrapper).getInstanceSupport());  
  10.   
  11.         // Acquire the filter mappings for this Context  
  12.         StandardContext context = (StandardContext) wrapper.getParent();  
  13.         FilterMap filterMaps[] = context.findFilterMaps();  
  14.   
  15.         // If there are no filter mappings, we are done  
  16.         if ((filterMaps == null) || (filterMaps.length == 0))  
  17.             return (filterChain);  
  18.   
  19.         // Acquire the information we will need to match filter mappings  
  20.         String servletName = wrapper.getName();  
  21.   
  22.         // Add the relevant path-mapped filters to this filter chain  
  23.         for (int i = 0; i < filterMaps.length; i++) {  
  24.             if (!matchDispatcher(filterMaps[i] ,dispatcher)) {  
  25.                 continue;  
  26.             }  
  27.             if (!matchFiltersURL(filterMaps[i], requestPath))  
  28.                 continue;  
  29.             ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)  
  30.                 context.findFilterConfig(filterMaps[i].getFilterName());  
  31.             if (filterConfig == null) {  
  32.                 ;       // FIXME - log configuration problem  
  33.                 continue;  
  34.             }  
  35.             boolean isCometFilter = false;  
  36.             if (comet) {  
  37.                 try {  
  38.                     isCometFilter = filterConfig.getFilter() instanceof CometFilter;  
  39.                 } catch (Exception e) {  
  40.                     // Note: The try catch is there because getFilter has a lot of   
  41.                     // declared exceptions. However, the filter is allocated much  
  42.                     // earlier  
  43.                 }  
  44.                 if (isCometFilter) {  
  45.                     filterChain.addFilter(filterConfig);  
  46.                 }  
  47.             } else {  
  48.                 filterChain.addFilter(filterConfig);  
  49.             }  
  50.         }  
  51.   
  52.        //省略部分代码  
  53.   
  54.         // Return the completed filter chain  
  55.         return (filterChain);  
  56.   
  57. }  




如果是<dispatcher>REQUEST</dispatcher>,那就不添加Filter,默认设置是REQUEST 
如果是<dispatcher>FORWARD</dispatcher>,添加Filter到FilterChain。 

第4步:调用doFilter或者service,代码删减了很多。 

org.apache.catalina.core.ApplicationFilterChain#doFilter(ServletRequest, ServletResponse):
 

Java代码    收藏代码
  1.   public void doFilter(ServletRequest request, ServletResponse response)throws IOException, ServletException {  
  2.             internalDoFilter(request,response);  
  3.   }  
  4.   
  5.   
  6. org.apache.catalina.core.ApplicationFilterChain#internalDoFilter(ServletRequest, ServletResponse)  
  7. private void internalDoFilter(ServletRequest request,   
  8.                                   ServletResponse response)  
  9.         throws IOException, ServletException {  
  10.   
  11.         // Call the next filter if there is one  
  12.         if (pos < n) {  
  13.                     filter.doFilter(request, response, this);  
  14.             return;  
  15.         }  
  16.        servlet.service((HttpServletRequest) request,(HttpServletResponse) response);  
  17.  
  18.     // //最终
  19.    Filter全部调用完毕后,就会把请求真正的传递给Servlet了,调用它的#service()方法。 
    servlet分别对应着对Servlet,JSP,和静态资源的处理。
    如果是真正Servlet比较简单,因为这里的"servlet"就是真实的Servlet实例了,
  20. 直接调用开发人员自己编写的#service()方法了
  21. (#service()内部是会调用#doGet(),#doPost()等方法的)。
     对于静态资源,是调用org.apache.catalina.servlets.DefaultServlet的#service()方法,由于DefaultServlet并没有重写这个方法,所以直接使用HttpServlet的#service()方法。但是DefaultServlet重写了#doGet(),#doPost()等方法(#doPost()内部又调用了#doGet()),
  22. 所以请求就又到了#doGet()这个方法中,DefaultServlet的#doGet()只调用了#serveResource()这个方法来提取资源,代码太长,就不再仔细的看了。
        如果是JSP资源,要调用org.apache.jasper.servlet.JspServlet的#service()方法进行
       // 获得请求的JSP文件的路径
     // 获取JspServletWrapper实例  然后调用org.apache.jasper.servlet.JspServletWrapper的service()方法
                 进行  // (1) 编译   // (2) 载入由jsp变成的Servlet类文件  // (3) 调用Servlet.service(req,resp)处理请求 
  23. }  






如果我对Filter非常了解的,根本就不需要花那么多时间去查看tomcat源代码。只要在web.xml增加一点配置就OK了。 

Java代码    收藏代码
  1. <filter-mapping>  
  2.         <filter-name>struts2</filter-name>  
  3.         <url-pattern>/*</url-pattern>  
  4.         <dispatcher>REQUEST</dispatcher>  
  5.         <dispatcher>FORWARD</dispatcher>  
  6. </filter-mapping>  

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值