Tomcat源码分析 ----- HTTP请求处理过程
Posted java-chen-hao
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Tomcat源码分析 ----- HTTP请求处理过程相关的知识,希望对你有一定的参考价值。
我们接着上一篇文章的容器处理来讲,当postParseRequest方法返回true时,则由容器继续处理,在service方法中有connector.getService().getContainer().getPipeline().getFirst().invoke(request, response)这一行:
- Connector调用getService()返回StandardService;
- StandardService调用getContainer返回StandardEngine;
- StandardEngine调用getPipeline返回与其关联的StandardPipeline;
Engine处理请求
我们在前面的文章中讲过StandardEngine
的构造函数为自己的Pipeline添加了基本阀StandardEngineValve
,代码如下:
public StandardEngine() super(); pipeline.setBasic(new StandardEngineValve()); try setJvmRoute(System.getProperty("jvmRoute")); catch(Exception ex) log.warn(sm.getString("standardEngine.jvmRouteFail"));
接下来我们看看StandardEngineValve
的invoke()
方法。该方法主要是选择合适的Host,然后调用Host中pipeline的第一个Valve的invoke()
方法。
public final void invoke(Request request, Response response) throws IOException, ServletException // Select the Host to be used for this Request Host host = request.getHost(); if (host == null) response.sendError (HttpServletResponse.SC_BAD_REQUEST, sm.getString("standardEngine.noHost", request.getServerName())); return; if (request.isAsyncSupported()) request.setAsyncSupported(host.getPipeline().isAsyncSupported()); // Ask this Host to process this request host.getPipeline().getFirst().invoke(request, response);
host.getPipeline().getFirst().invoke(request, response)
,可以看到 Host 容器先获取自己的管道,再获取第一个阀门,我们再看看该阀门的 invoke 方法。Host处理请求
分析Host的时候,我们从Host的构造函数入手,该方法主要是设置基础阀门。public StandardHost() super(); pipeline.setBasic(new StandardHostValve());
StandardPipeline调用getFirst得到第一个阀去处理请求,由于基本阀是最后一个,所以最后会由基本阀去处理请求。
StandardHost的Pipeline里面一定有 ErrorReportValve 与 StandardHostValve两个Valve,ErrorReportValve主要是检测 Http 请求过程中是否出现过什么异常, 有异常的话, 直接拼装 html 页面, 输出到客户端。
我们看看ErrorReportValve的invoke方法:
public void invoke(Request request, Response response) throws IOException, ServletException // Perform the request // 1. 先将 请求转发给下一个 Valve getNext().invoke(request, response); // 2. 这里的 isCommitted 表明, 请求是正常处理结束 if (response.isCommitted()) return; // 3. 判断请求过程中是否有异常发生 Throwable throwable = (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION); if (request.isAsyncStarted() && ((response.getStatus() < 400 && throwable == null) || request.isAsyncDispatching())) return; if (throwable != null) // The response is an error response.setError(); // Reset the response (if possible) try // 4. 重置 response 里面的数据(此时 Response 里面可能有些数据) response.reset(); catch (IllegalStateException e) // Ignore // 5. 这就是我们常看到的 500 错误码 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); response.setSuspended(false); try // 6. 这里就是将 异常的堆栈信息组合成 html 页面, 输出到前台 report(request, response, throwable); catch (Throwable tt) ExceptionUtils.handleThrowable(tt); if (request.isAsyncStarted()) // 7. 若是异步请求的话, 设置对应的 complete (对应的是 异步 Servlet) request.getAsyncContext().complete();
该方法首先执行了下个阀门的 invoke 方法。然后根据返回的Request 属性设置一些错误信息。那么下个阀门是谁呢?其实就是基础阀门了:StandardHostValve,该阀门的 invoke 的方法是如何实现的呢?
@Override public final void invoke(Request request, Response response) throws IOException, ServletException // Select the Context to be used for this Request Context context = request.getContext(); if (context == null) response.sendError (HttpServletResponse.SC_INTERNAL_SERVER_ERROR, sm.getString("standardHost.noContext")); return; // Bind the context CL to the current thread if( context.getLoader() != null ) // Not started - it should check for availability first // This should eventually move to Engine, it‘s generic. if (Globals.IS_SECURITY_ENABLED) PrivilegedAction<Void> pa = new PrivilegedSetTccl( context.getLoader().getClassLoader()); AccessController.doPrivileged(pa); else Thread.currentThread().setContextClassLoader (context.getLoader().getClassLoader()); if (request.isAsyncSupported()) request.setAsyncSupported(context.getPipeline().isAsyncSupported()); // Don‘t fire listeners during async processing // If a request init listener throws an exception, the request is // aborted boolean asyncAtStart = request.isAsync(); // An async error page may dispatch to another resource. This flag helps // ensure an infinite error handling loop is not entered boolean errorAtStart = response.isError(); if (asyncAtStart || context.fireRequestInitEvent(request)) // Ask this Context to process this request try context.getPipeline().getFirst().invoke(request, response); catch (Throwable t) ExceptionUtils.handleThrowable(t); if (errorAtStart) container.getLogger().error("Exception Processing " + request.getRequestURI(), t); else request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, t); throwable(request, response, t); // If the request was async at the start and an error occurred then // the async error handling will kick-in and that will fire the // request destroyed event *after* the error handling has taken // place if (!(request.isAsync() || (asyncAtStart && request.getAttribute( RequestDispatcher.ERROR_EXCEPTION) != null))) // Protect against NPEs if context was destroyed during a // long running request. if (context.getState().isAvailable()) if (!errorAtStart) // Error page processing response.setSuspended(false); Throwable t = (Throwable) request.getAttribute( RequestDispatcher.ERROR_EXCEPTION); if (t != null) throwable(request, response, t); else status(request, response); context.fireRequestDestroyEvent(request); // Access a session (if present) to update last accessed time, based on a // strict interpretation of the specification if (ACCESS_SESSION) request.getSession(false); // Restore the context classloader if (Globals.IS_SECURITY_ENABLED) PrivilegedAction<Void> pa = new PrivilegedSetTccl( StandardHostValve.class.getClassLoader()); AccessController.doPrivileged(pa); else Thread.currentThread().setContextClassLoader (StandardHostValve.class.getClassLoader());
Context处理请求
接着Context会去处理请求,同理,StandardContextValve的invoke方法会被调用:
@Override public final void invoke(Request request, Response response) throws IOException, ServletException // Disallow any direct access to resources under WEB-INF or META-INF MessageBytes requestPathMB = request.getRequestPathMB(); if ((requestPathMB.startsWithIgnoreCase("/META-INF/", 0)) || (requestPathMB.equalsIgnoreCase("/META-INF")) || (requestPathMB.startsWithIgnoreCase("/WEB-INF/", 0)) || (requestPathMB.equalsIgnoreCase("/WEB-INF"))) response.sendError(HttpServletResponse.SC_NOT_FOUND); return; // Select the Wrapper to be used for this Request Wrapper wrapper = request.getWrapper(); if (wrapper == null || wrapper.isUnavailable()) response.sendError(HttpServletResponse.SC_NOT_FOUND); return; // Acknowledge the request try response.sendAcknowledgement(); catch (IOException ioe) container.getLogger().error(sm.getString( "standardContextValve.acknowledgeException"), ioe); request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, ioe); response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); return; if (request.isAsyncSupported()) request.setAsyncSupported(wrapper.getPipeline().isAsyncSupported()); wrapper.getPipeline().getFirst().invoke(request, response);
Wrapper处理请求
Wrapper是一个Servlet的包装,我们先来看看构造方法。主要作用就是设置基础阀门StandardWrapperValve
。
public StandardWrapper() super(); swValve=new StandardWrapperValve(); pipeline.setBasic(swValve); broadcaster = new NotificationBroadcasterSupport();
接下来我们看看StandardWrapperValve
的invoke()
方法。
@Override public final void invoke(Request request, Response response) throws IOException, ServletException // Initialize local variables we may need boolean unavailable = false; Throwable throwable = null; // This should be a Request attribute... long t1=System.currentTimeMillis(); requestCount.incrementAndGet(); StandardWrapper wrapper = (StandardWrapper) getContainer(); Servlet servlet = null; Context context = (Context) wrapper.getParent(); // Check for the application being marked unavailable if (!context.getState().isAvailable()) response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, sm.getString("standardContext.isUnavailable")); unavailable = true; // Check for the servlet being marked unavailable if (!unavailable && wrapper.isUnavailable()) container.getLogger().info(sm.getString("standardWrapper.isUnavailable", wrapper.getName())); long available = wrapper.getAvailable(); if ((available > 0L) && (available < Long.MAX_VALUE)) response.setDateHeader("Retry-After", available); response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, sm.getString("standardWrapper.isUnavailable", wrapper.getName())); else if (available == Long.MAX_VALUE) response.sendError(HttpServletResponse.SC_NOT_FOUND, sm.getString("standardWrapper.notFound", wrapper.getName())); unavailable = true; // Allocate a servlet instance to process this request try // 关键点1:这儿调用Wrapper的allocate()方法分配一个Servlet实例 if (!unavailable) servlet = wrapper.allocate(); catch (UnavailableException e) container.getLogger().error( sm.getString("standardWrapper.allocateException", wrapper.getName()), e); long available = wrapper.getAvailable(); if ((available > 0L) && (available < Long.MAX_VALUE)) response.setDateHeader("Retry-After", available); response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, sm.getString("standardWrapper.isUnavailable", wrapper.getName())); else if (available == Long.MAX_VALUE) response.sendError(HttpServletResponse.SC_NOT_FOUND, sm.getString("standardWrapper.notFound", wrapper.getName())); catch (ServletException e) container.getLogger().error(sm.getString("standardWrapper.allocateException", wrapper.getName()), StandardWrapper.getRootCause(e)); throwable = e; exception(request, response, e); catch (Throwable e) ExceptionUtils.handleThrowable(e); container.getLogger().error(sm.getString("standardWrapper.allocateException", wrapper.getName()), e); throwable = e; exception(request, response, e); servlet = null; MessageBytes requestPathMB = request.getRequestPathMB(); DispatcherType dispatcherType = DispatcherType.REQUEST; if (request.getDispatcherType()==DispatcherType.ASYNC) dispatcherType = DispatcherType.ASYNC; request.setAttribute(Globals.DISPATCHER_TYPE_ATTR,dispatcherType); request.setAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR, requestPathMB); // Create the filter chain for this request // 关键点2,创建过滤器链,类似于Pipeline的功能 ApplicationFilterChain filterChain = ApplicationFilterFactory.createFilterChain(request, wrapper, servlet); // Call the filter chain for this request // NOTE: This also calls the servlet‘s service() method try if ((servlet != null) && (filterChain != null)) // Swallow output if needed if (context.getSwallowOutput()) try SystemLogHandler.startCapture(); if (request.isAsyncDispatching()) request.getAsyncContextInternal().doInternalDispatch(); else // 关键点3,调用过滤器链的doFilter,最终会调用到Servlet的service方法 filterChain.doFilter(request.getRequest(), response.getResponse()); finally String log = SystemLogHandler.stopCapture(); if (log != null && log.length() > 0) context.getLogger().info(log); else if (request.isAsyncDispatching()) request.getAsyncContextInternal().doInternalDispatch(); else // 关键点3,调用过滤器链的doFilter,最终会调用到Servlet的service方法 filterChain.doFilter (request.getRequest(), response.getResponse()); catch (ClientAbortException e) throwable = e; exception(request, response, e); catch (IOException e) container.getLogger().error(sm.getString( "standardWrapper.serviceException", wrapper.getName(), context.getName()), e); throwable = e; exception(request, response, e); catch (UnavailableException e) container.getLogger().error(sm.getString( "standardWrapper.serviceException", wrapper.getName(), context.getName()), e); // throwable = e; // exception(request, response, e); wrapper.unavailable(e); long available = wrapper.getAvailable(); if ((available > 0L) && (available < Long.MAX_VALUE)) response.setDateHeader("Retry-After", available); response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, sm.getString("standardWrapper.isUnavailable", wrapper.getName())); else if (available == Long.MAX_VALUE) response.sendError(HttpServletResponse.SC_NOT_FOUND, sm.getString("standardWrapper.notFound", wrapper.getName())); // Do not save exception in ‘throwable‘, because we // do not want to do exception(request, response, e) processing catch (ServletException e) Throwable rootCause = StandardWrapper.getRootCause(e); if (!(rootCause instanceof ClientAbortException)) container.getLogger().error(sm.getString( "standardWrapper.serviceExceptionRoot", wrapper.getName(), context.getName(), e.getMessage()), rootCause); throwable = e; exception(request, response, e); catch (Throwable e) ExceptionUtils.handleThrowable(e); container.getLogger().error(sm.getString( "standardWrapper.serviceException", wrapper.getName(), context.getName()), e); throwable = e; exception(request, response, e); // Release the filter chain (if any) for this request // 关键点4,释放掉过滤器链及其相关资源 if (filterChain != null) filterChain.release(); // 关键点5,释放掉Servlet及相关资源 // Deallocate the allocated servlet instance try if (servlet != null) wrapper.deallocate(servlet); catch (Throwable e) ExceptionUtils.handleThrowable(e); container.getLogger().error(sm.getString("standardWrapper.deallocateException", wrapper.getName()), e); if (throwable == null) throwable = e; exception(request, response, e); // If this servlet has been marked permanently unavailable, // unload it and release this instance // 关键点6,如果servlet被标记为永远不可达,则需要卸载掉它,并释放这个servlet实例 try if ((servlet != null) && (wrapper.getAvailable() == Long.MAX_VALUE)) wrapper.unload(); catch (Throwable e) ExceptionUtils.handleThrowable(e); container.getLogger().error(sm.getString("standardWrapper.unloadException", wrapper.getName()), e); if (throwable == null) throwable = e; exception(request, response, e); long t2=System.currentTimeMillis(); long time=t2-t1; processingTime += time; if( time > maxTime) maxTime=time; if( time < minTime) minTime=time;
通过阅读源码,我们发现了几个关键点。现罗列如下,后面我们会逐一分析这些关键点相关的源码。
- 关键点1:这儿调用Wrapper的allocate()方法分配一个Servlet实例
- 关键点2,创建过滤器链,类似于Pipeline的功能
- 关键点3,调用过滤器链的doFilter,最终会调用到Servlet的service方法
- 关键点4,释放掉过滤器链及其相关资源
- 关键点5,释放掉Servlet及相关资源
- 关键点6,如果servlet被标记为永远不可达,则需要卸载掉它,并释放这个servlet实例
关键点1 - Wrapper分配Servlet实例
我们来分析一下Wrapper.allocate()方法
@Override public Servlet allocate() throws ServletException // If we are currently unloading this servlet, throw an exception // 卸载过程中,不能分配Servlet if (unloading) throw new ServletException(sm.getString("standardWrapper.unloading", getName())); boolean newInstance = false; // If not SingleThreadedModel, return the same instance every time // 如果Wrapper没有实现SingleThreadedModel,则每次都会返回同一个Servlet if (!singleThreadModel) // Load and initialize our instance if necessary // 实例为null或者实例还未初始化,使用synchronized来保证并发时的原子性 if (instance == null || !instanceInitialized) synchronized (this) if (instance == null) try if (log.isDebugEnabled()) log.debug("Allocating non-STM instance"); // Note: We don‘t know if the Servlet implements // SingleThreadModel until we have loaded it. // 加载Servlet instance = loadServlet(); newInstance = true; if (!singleThreadModel) // For non-STM, increment here to prevent a race // condition with unload. Bug 43683, test case // #3 countAllocated.incrementAndGet(); catch (ServletException e) throw e; catch (Throwable e) ExceptionUtils.handleThrowable(e); throw new ServletException(sm.getString("standardWrapper.allocate"), e); // 初始化Servlet if (!instanceInitialized) initServlet(instance); if (singleThreadModel) if (newInstance) // Have to do this outside of the sync above to prevent a // possible deadlock synchronized (instancePool) instancePool.push(instance); nInstances++; // 非单线程模型,直接返回已经创建的Servlet,也就是说,这种情况下只会创建一个Servlet else if (log.isTraceEnabled()) log.trace(" Returning non-STM instance"); // For new instances, count will have been incremented at the // time of creation if (!newInstance) countAllocated.incrementAndGet(); return instance; // 如果是单线程模式,则使用servlet对象池技术来加载多个Servlet synchronized (instancePool) while (countAllocated.get() >= nInstances) // Allocate a new instance if possible, or else wait if (nInstances < maxInstances) try instancePool.push(loadServlet()); nInstances++; catch (ServletException e) throw e; catch (Throwable e) ExceptionUtils.handleThrowable(e); throw new ServletException(sm.getString("standardWrapper.allocate"), e); else try instancePool.wait(); catch (InterruptedException e) // Ignore if (log.isTraceEnabled()) log.trace(" Returning allocated STM instance"); countAllocated.incrementAndGet(); return instancePool.pop();
总结下来,注意以下几点即可:
- 卸载过程中,不能分配Servlet
- 如果不是单线程模式,则每次都会返回同一个Servlet(默认Servlet实现方式)
Servlet
实例为null
或者Servlet
实例还未初始化
,使用synchronized来保证并发时的原子性- 如果是单线程模式,则使用servlet对象池技术来加载多个Servlet
接下来我们看看loadServlet()
方法
public synchronized Servlet loadServlet() throws ServletException // Nothing to do if we already have an instance or an instance pool if (!singleThreadModel && (instance != null)) return instance; PrintStream out = System.out; if (swallowOutput) SystemLogHandler.startCapture(); Servlet servlet; try long t1=System.currentTimeMillis(); // Complain if no servlet class has been specified if (servletClass == null) unavailable(null); throw new ServletException (sm.getString("standardWrapper.notClass", getName())); // 关键的地方,就是通过实例管理器,创建Servlet实例,而实例管理器是通过特殊的类加载器来加载给定的类 InstanceManager instanceManager = ((StandardContext)getParent()).getInstanceManager(); try servlet = (Servlet) instanceManager.newInstance(servletClass); catch (ClassCastException e) unavailable(null); // Restore the context ClassLoader throw new ServletException (sm.getString("standardWrapper.notServlet", servletClass), e); catch (Throwable e) e = ExceptionUtils.unwrapInvocationTargetException(e); ExceptionUtils.handleThrowable(e); unavailable(null); // Added extra log statement for Bugzilla 36630: // https://bz.apache.org/bugzilla/show_bug.cgi?id=36630 if(log.isDebugEnabled()) log.debug(sm.getString("standardWrapper.instantiate", servletClass), e); // Restore the context ClassLoader throw new ServletException (sm.getString("standardWrapper.instantiate", servletClass), e); if (multipartConfigElement == null) MultipartConfig annotation = servlet.getClass().getAnnotation(MultipartConfig.class); if (annotation != null) multipartConfigElement = new MultipartConfigElement(annotation); // Special handling for ContainerServlet instances // Note: The InstanceManager checks if the application is permitted // to load ContainerServlets if (servlet instanceof ContainerServlet) ((ContainerServlet) servlet).setWrapper(this); classLoadTime=(int) (System.currentTimeMillis() -t1); if (servlet instanceof SingleThreadModel) if (instancePool == null) instancePool = new Stack<>(); singleThreadModel = true; // 调用Servlet的init方法 initServlet(servlet); fireContainerEvent("load", this); loadTime=System.currentTimeMillis() -t1; finally if (swallowOutput) String log = SystemLogHandler.stopCapture(); if (log != null && log.length() > 0) if (getServletContext() != null) getServletContext().log(log); else out.println(log); return servlet;
关键的地方有两个:
- 通过实例管理器,创建Servlet实例,而实例管理器是通过特殊的类加载器来加载给定的类
- 调用Servlet的init方法
关键点2 - 创建过滤器链
创建过滤器链是调用的org.apache.catalina.core.ApplicationFilterFactory
的createFilterChain()
方法。我们来分析一下这个方法。该方法需要注意的地方已经在代码的comments里面说明了。
public static ApplicationFilterChain createFilterChain(ServletRequest request, Wrapper wrapper, Servlet servlet) // If there is no servlet to execute, return null if (servlet == null) return null; // Create and initialize a filter chain object // 1. 如果加密打开了,则可能会多次调用这个方法 // 2. 为了避免重复生成filterChain对象,所以会将filterChain对象放在Request里面进行缓存 ApplicationFilterChain filterChain = null; if (request instanceof Request) Request req = (Request) request; if (Globals.IS_SECURITY_ENABLED) // Security: Do not recycle filterChain = new ApplicationFilterChain(); else filterChain = (ApplicationFilterChain) req.getFilterChain(); if (filterChain == null) filterChain = new ApplicationFilterChain(); req.setFilterChain(filterChain); else // Request dispatcher in use filterChain = new ApplicationFilterChain(); filterChain.setServlet(servlet); filterChain.setServletSupportsAsync(wrapper.isAsyncSupported()); // Acquire the filter mappings for this Context StandardContext context = (StandardContext) wrapper.getParent(); // 从这儿看出过滤器链对象里面的元素是根据Context里面的filterMaps来生成的 FilterMap filterMaps[] = context.findFilterMaps(); // If there are no filter mappings, we are done if ((filterMaps == null) || (filterMaps.length == 0)) return (filterChain); // Acquire the information we will need to match filter mappings 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(); // Add the relevant path-mapped filters to this filter chain // 类型和路径都匹配的情况下,将context.filterConfig放到过滤器链里面 for (int i = 0; i < filterMaps.length; i++) if (!matchDispatcher(filterMaps[i] ,dispatcher)) continue; if (!matchFiltersURL(filterMaps[i], requestPath)) continue; ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) context.findFilterConfig(filterMaps[i].getFilterName()); if (filterConfig == null) // FIXME - log configuration problem continue; filterChain.addFilter(filterConfig); // Add filters that match on servlet name second // 类型和servlet名称都匹配的情况下,将context.filterConfig放到过滤器链里面 for (int i = 0; i < filterMaps.length; i++) if (!matchDispatcher(filterMaps[i] ,dispatcher)) continue; if (!matchFiltersServlet(filterMaps[i], servletName)) continue; ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) context.findFilterConfig(filterMaps[i].getFilterName()); if (filterConfig == null) // FIXME - log configuration problem continue; filterChain.addFilter(filterConfig); // Return the completed filter chain return filterChain;
关键点3 - 调用过滤器链的doFilter
ApplicationFilterChain类的doFilter函数代码如下,它会将处理委托给internalDoFilter函数。
@Override public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException if( Globals.IS_SECURITY_ENABLED ) final ServletRequest req = request; final ServletResponse res = response; try java.security.AccessController.doPrivileged( new java.security.PrivilegedExceptionAction<Void>() @Override public Void 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);
ApplicationFilterChain类的internalDoFilter函数代码如下:
// 1. `internalDoFilter`方法通过pos和n来调用过滤器链里面的每个过滤器。pos表示当前的过滤器下标,n表示总的过滤器数量 // 2. `internalDoFilter`方法最终会调用servlet.service()方法 private void internalDoFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException // Call the next filter if there is one // 1. 当pos小于n时, 则执行Filter if (pos < n) // 2. 得到 过滤器 Filter,执行一次post++ ApplicationFilterConfig filterConfig = filters[pos++]; try Filter filter = filterConfig.getFilter(); if (request.isAsyncSupported() && "false".equalsIgnoreCase( filterConfig.getFilterDef().getAsyncSupported())) request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE); 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 // 4. 这里的 filter 的执行 有点递归的感觉, 通过 pos 来控制从 filterChain 里面拿出那个 filter 来进行操作 // 这里把this(filterChain)传到自定义filter里面,我们自定义的filter,会重写doFilter,在这里会被调用,doFilter里面会执行业务逻辑,如果执行业务逻辑成功,则会调用 filterChain.doFilter(servletRequest, servletResponse); ,filterChain就是这里传过去的this;如果业务逻辑执行失败,则return,filterChain终止,后面的servlet.service(request, response)也不会执行了 // 所以在 Filter 里面所调用 return, 则会终止 Filter 的调用, 而下面的 Servlet.service 更本就没有调用到 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 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 //当pos等于n时,过滤器都执行完毕,终于执行了熟悉的servlet.service(request, response)方法。 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
@WebFilter(urlPatterns = "/*", filterName = "myfilter") public class FileterController implements Filter @Override public void init(FilterConfig filterConfig) throws ServletException System.out.println("Filter初始化中"); @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException System.out.println("登录逻辑"); if("登录失败") response.getWriter().write("登录失败"); //后面的拦截器和servlet都不会执行了 return; //登录成功,执行下一个过滤器 filterChain.doFilter(servletRequest, servletResponse); @Override public void destroy() System.out.println("Filter销毁中");
- pos和n是ApplicationFilterChain的成员变量,分别表示过滤器链的当前位置和过滤器总数,所以当pos小于n时,会不断执行ApplicationFilterChain的doFilter方法;
- 当pos等于n时,过滤器都执行完毕,终于执行了熟悉的servlet.service(request, response)方法。
以上是关于Tomcat源码分析 ----- HTTP请求处理过程的主要内容,如果未能解决你的问题,请参考以下文章