当前位置:   article > 正文

Tomcat对HTTP请求的处理(三)

getnext().invoke(request,response);

摘要:本文主要介绍了tomcat内部处理HTTP请求的Container部分,即剩余的部分

上一篇文章讲到CoyoteAdapter对HTTP请求的处理,主要查看了postParseRequest()方法对request的处理填充。我们继续往下看:

  1. //代码清单1
  2. // Parse and set Catalina and configuration specific
  3. // request parameters
  4. req.getRequestProcessor().setWorkerThreadName(Thread.currentThread().getName());
  5. postParseSuccess = postParseRequest(req, request, res, response);
  6. if (postParseSuccess) {
  7. //check valves if we support async
  8. request.setAsyncSupported(connector.getService().getContainer().getPipeline().isAsyncSupported());
  9. // Calling the container
  10. //111
  11. connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
  12. //..略代码
  13. }

上一篇文章分析过了,标注1的地方最终调用的是StandardEngineValveinvoke()方法:

  1. //代码清单2
  2. @Override
  3. public final void invoke(Request request, Response response)
  4. throws IOException, ServletException {
  5. // Select the Host to be used for this Request
  6. Host host = request.getHost();
  7. if (host == null) {
  8. response.sendError
  9. (HttpServletResponse.SC_BAD_REQUEST,
  10. sm.getString("standardEngine.noHost",
  11. request.getServerName()));
  12. return;
  13. }
  14. if (request.isAsyncSupported()) {
  15. request.setAsyncSupported(host.getPipeline().isAsyncSupported());
  16. }
  17. // Ask this Host to process this request
  18. //调用host的pipeline 来处理
  19. //11111111
  20. host.getPipeline().getFirst().invoke(request, response);
  21. }

在清单2的标注1的地方我们可以看到最后调用的是hostpipeline来处理,而StandardHostStandardEngine则有所不同,不同的地方在于,StandardEngine只有一个基本阀也就是StandardEngineValve,而StandardHost除了基本阀门StandardHostValve还额外有两个阀门分别是AccessLogValveErrorReportValve。这两个阀门的来源分别是server.xml中配置以及在StandardHoststartInternal()方法中添加。所以标注1的地方getFirst()返回的应该是AccessLogValve这个类的实例,至于为什么是AccessLogValve不是ErrorReportValve,这个大家可以自己思考下,下面我们继续查看AccessLogValveinvoke()方法:

  1. //代码清单3
  2. @Override
  3. public void invoke(Request request, Response response) throws IOException,
  4. ServletException {
  5. getNext().invoke(request, response);
  6. }

这里的getNext()返回的应该是ErrorReportValve,继续查看其invoke()方法:

  1. //代码清单4
  2. @Override
  3. public void invoke(Request request, Response response) throws IOException, ServletException {
  4. //111111
  5. // Perform the request
  6. getNext().invoke(request, response);
  7. if (response.isCommitted()) {
  8. if (response.setErrorReported()) {
  9. // Error wasn't previously reported but we can't write an error
  10. // page because the response has already been committed. Attempt
  11. // to flush any data that is still to be written to the client.
  12. try {
  13. response.flushBuffer();
  14. } catch (Throwable t) {
  15. ExceptionUtils.handleThrowable(t);
  16. }
  17. // Close immediately to signal to the client that something went
  18. // wrong
  19. response.getCoyoteResponse().action(ActionCode.CLOSE_NOW, null);
  20. }
  21. return;
  22. }
  23. Throwable throwable = (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);
  24. // If an async request is in progress and is not going to end once this
  25. // container thread finishes, do not trigger error page handling - it
  26. // will be triggered later if required.
  27. if (request.isAsync() && !request.isAsyncCompleting()) {
  28. return;
  29. }
  30. if (throwable != null && !response.isError()) {
  31. // Make sure that the necessary methods have been called on the
  32. // response. (It is possible a component may just have set the
  33. // Throwable. Tomcat won't do that but other components might.)
  34. // These are safe to call at this point as we know that the response
  35. // has not been committed.
  36. response.reset();
  37. response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
  38. }
  39. // One way or another, response.sendError() will have been called before
  40. // execution reaches this point and suspended the response. Need to
  41. // reverse that so this valve can write to the response.
  42. response.setSuspended(false);
  43. try {
  44. report(request, response, throwable);
  45. } catch (Throwable tt) {
  46. ExceptionUtils.handleThrowable(tt);
  47. }
  48. }

可以看到在方法一开始也就是标注1的地方继续是调用getNext()然后调用其invoke()方法,下面的代码可以考虑为后续处理,所以我们继续往下看,也就是StandardHostValveinvoke()方法:

  1. //代码清单5
  2. @Override
  3. public final void invoke(Request request, Response response)
  4. throws IOException, ServletException {
  5. // Select the Context to be used for this Request
  6. // 获取处理这个request的context对象
  7. Context context = request.getContext();
  8. if (context == null) {
  9. response.sendError
  10. (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
  11. sm.getString("standardHost.noContext"));
  12. return;
  13. }
  14. // Bind the context CL to the current thread
  15. if( context.getLoader() != null ) {
  16. // Not started - it should check for availability first
  17. // This should eventually move to Engine, it's generic.
  18. if (Globals.IS_SECURITY_ENABLED) {
  19. PrivilegedAction<Void> pa = new PrivilegedSetTccl(
  20. context.getLoader().getClassLoader());
  21. AccessController.doPrivileged(pa);
  22. } else {
  23. Thread.currentThread().setContextClassLoader
  24. (context.getLoader().getClassLoader());
  25. }
  26. }
  27. if (request.isAsyncSupported()) {
  28. request.setAsyncSupported(context.getPipeline().isAsyncSupported());
  29. }
  30. boolean asyncAtStart = request.isAsync();
  31. boolean asyncDispatching = request.isAsyncDispatching();
  32. if (asyncAtStart || context.fireRequestInitEvent(request)) {
  33. // Ask this Context to process this request. Requests that are in
  34. // async mode and are not being dispatched to this resource must be
  35. // in error and have been routed here to check for application
  36. // defined error pages.
  37. try {
  38. if (!asyncAtStart || asyncDispatching) {
  39. //1111111
  40. //调用Context的pipeline来处理
  41. context.getPipeline().getFirst().invoke(request, response);
  42. } else {
  43. // Make sure this request/response is here because an error
  44. // report is required.
  45. if (!response.isErrorReportRequired()) {
  46. throw new IllegalStateException(sm.getString("standardHost.asyncStateError"));
  47. }
  48. }
  49. } catch (Throwable t) {
  50. ExceptionUtils.handleThrowable(t);
  51. container.getLogger().error("Exception Processing " + request.getRequestURI(), t);
  52. // If a new error occurred while trying to report a previous
  53. // error allow the original error to be reported.
  54. if (!response.isErrorReportRequired()) {
  55. request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, t);
  56. throwable(request, response, t);
  57. }
  58. }
  59. // Now that the request/response pair is back under container
  60. // control lift the suspension so that the error handling can
  61. // complete and/or the container can flush any remaining data
  62. response.setSuspended(false);
  63. Throwable t = (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);
  64. // Protect against NPEs if the context was destroyed during a
  65. // long running request.
  66. if (!context.getState().isAvailable()) {
  67. return;
  68. }
  69. // Look for (and render if found) an application level error page
  70. if (response.isErrorReportRequired()) {
  71. if (t != null) {
  72. throwable(request, response, t);
  73. } else {
  74. status(request, response);
  75. }
  76. }
  77. if (!request.isAsync() && (!asyncAtStart || !response.isErrorReportRequired())) {
  78. context.fireRequestDestroyEvent(request);
  79. }
  80. }
  81. // Access a session (if present) to update last accessed time, based on a
  82. // strict interpretation of the specification
  83. if (ACCESS_SESSION) {
  84. request.getSession(false);
  85. }
  86. // Restore the context classloader
  87. if (Globals.IS_SECURITY_ENABLED) {
  88. PrivilegedAction<Void> pa = new PrivilegedSetTccl(
  89. StandardHostValve.class.getClassLoader());
  90. AccessController.doPrivileged(pa);
  91. } else {
  92. Thread.currentThread().setContextClassLoader
  93. (StandardHostValve.class.getClassLoader());
  94. }
  95. }

代码比较长,先获取了该需要处理该request的Context实例,然后调用了该实例的pipeline来处理request。而StandardContext对象在初始化的时候如果没有在server.xml中配置Valve阀门的话,那么ContextgetFirst()方法返回的是StandardContextValve的实例,所以查看StandardContextValveinvoke()方法:

  1. //代码清单6
  2. @Override
  3. public final void invoke(Request request, Response response)
  4. throws IOException, ServletException {
  5. // Disallow any direct access to resources under WEB-INF or META-INF
  6. MessageBytes requestPathMB = request.getRequestPathMB();
  7. if ((requestPathMB.startsWithIgnoreCase("/META-INF/", 0))
  8. || (requestPathMB.equalsIgnoreCase("/META-INF"))
  9. || (requestPathMB.startsWithIgnoreCase("/WEB-INF/", 0))
  10. || (requestPathMB.equalsIgnoreCase("/WEB-INF"))) {
  11. response.sendError(HttpServletResponse.SC_NOT_FOUND);
  12. return;
  13. }
  14. // Select the Wrapper to be used for this Request
  15. Wrapper wrapper = request.getWrapper();
  16. if (wrapper == null || wrapper.isUnavailable()) {
  17. response.sendError(HttpServletResponse.SC_NOT_FOUND);
  18. return;
  19. }
  20. // Acknowledge the request
  21. try {
  22. response.sendAcknowledgement();
  23. } catch (IOException ioe) {
  24. container.getLogger().error(sm.getString(
  25. "standardContextValve.acknowledgeException"), ioe);
  26. request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, ioe);
  27. response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
  28. return;
  29. }
  30. if (request.isAsyncSupported()) {
  31. request.setAsyncSupported(wrapper.getPipeline().isAsyncSupported());
  32. }
  33. //调用wrapper的pipeline来处理
  34. //11111
  35. wrapper.getPipeline().getFirst().invoke(request, response);
  36. }

从标注1的地方可以看到最终调用的还是子容器StandardWrapperpipeline来处理request,也就是StandardWrapperValveinvoke()方法:

  1. //代码清单7
  2. @Override
  3. public final void invoke(Request request, Response response)
  4. throws IOException, ServletException {
  5. // Initialize local variables we may need
  6. boolean unavailable = false;
  7. Throwable throwable = null;
  8. // This should be a Request attribute...
  9. long t1=System.currentTimeMillis();
  10. requestCount++;
  11. StandardWrapper wrapper = (StandardWrapper) getContainer();
  12. Servlet servlet = null;
  13. Context context = (Context) wrapper.getParent();
  14. // Check for the application being marked unavailable
  15. if (!context.getState().isAvailable()) {
  16. response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
  17. sm.getString("standardContext.isUnavailable"));
  18. unavailable = true;
  19. }
  20. // Check for the servlet being marked unavailable
  21. if (!unavailable && wrapper.isUnavailable()) {
  22. container.getLogger().info(sm.getString("standardWrapper.isUnavailable",
  23. wrapper.getName()));
  24. long available = wrapper.getAvailable();
  25. if ((available > 0L) && (available < Long.MAX_VALUE)) {
  26. response.setDateHeader("Retry-After", available);
  27. response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
  28. sm.getString("standardWrapper.isUnavailable",
  29. wrapper.getName()));
  30. } else if (available == Long.MAX_VALUE) {
  31. response.sendError(HttpServletResponse.SC_NOT_FOUND,
  32. sm.getString("standardWrapper.notFound",
  33. wrapper.getName()));
  34. }
  35. unavailable = true;
  36. }
  37. // Allocate a servlet instance to process this request
  38. try {
  39. if (!unavailable) {
  40. //加载servlet
  41. //111111111111
  42. servlet = wrapper.allocate();
  43. }
  44. } catch (UnavailableException e) {
  45. //异常处理 略
  46. } catch (ServletException e) {
  47. //异常处理 略
  48. } catch (Throwable e) {
  49. //异常处理 略
  50. }
  51. // Identify if the request is Comet related now that the servlet has been allocated
  52. boolean comet = false;
  53. if (servlet instanceof CometProcessor && Boolean.TRUE.equals(request.getAttribute(
  54. Globals.COMET_SUPPORTED_ATTR))) {
  55. comet = true;
  56. request.setComet(true);
  57. }
  58. MessageBytes requestPathMB = request.getRequestPathMB();
  59. DispatcherType dispatcherType = DispatcherType.REQUEST;
  60. if (request.getDispatcherType()==DispatcherType.ASYNC) dispatcherType = DispatcherType.ASYNC;
  61. request.setAttribute(Globals.DISPATCHER_TYPE_ATTR,dispatcherType);
  62. request.setAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR,
  63. requestPathMB);
  64. // Create the filter chain for this request
  65. //2222222 创建filterChain
  66. ApplicationFilterFactory factory = ApplicationFilterFactory.getInstance();
  67. ApplicationFilterChain filterChain = factory.createFilterChain(request, wrapper, servlet);
  68. // Reset comet flag value after creating the filter chain
  69. request.setComet(false);
  70. // Call the filter chain for this request
  71. // NOTE: This also calls the servlet's service() method
  72. try {
  73. if ((servlet != null) && (filterChain != null)) {
  74. // Swallow output if needed
  75. if (context.getSwallowOutput()) {
  76. try {
  77. SystemLogHandler.startCapture();
  78. if (request.isAsyncDispatching()) {
  79. //TODO SERVLET3 - async
  80. ((AsyncContextImpl)request.getAsyncContext()).doInternalDispatch();
  81. } else if (comet) {
  82. filterChain.doFilterEvent(request.getEvent());
  83. request.setComet(true);
  84. } else {
  85. filterChain.doFilter(request.getRequest(),response.getResponse());
  86. }
  87. } finally {
  88. String log = SystemLogHandler.stopCapture();
  89. if (log != null && log.length() > 0) {
  90. context.getLogger().info(log);
  91. }
  92. }
  93. } else {
  94. if (request.isAsyncDispatching()) {
  95. //TODO SERVLET3 - async
  96. ((AsyncContextImpl)request.getAsyncContext()).doInternalDispatch();
  97. } else if (comet) {
  98. request.setComet(true);
  99. filterChain.doFilterEvent(request.getEvent());
  100. } else {
  101. //3333333333 调用fiterChain来处理 request 和 response
  102. filterChain.doFilter(request.getRequest(), response.getResponse());
  103. }
  104. }
  105. }
  106. } catch (ClientAbortException e) {
  107. //异常处理 略
  108. exception(request, response, e);
  109. } catch (IOException e) {
  110. //异常处理 略
  111. } catch (UnavailableException e) {
  112. //异常处理 略
  113. } catch (ServletException e) {
  114. //异常处理 略
  115. } catch (Throwable e) {
  116. //异常处理 略
  117. }
  118. // Release the filter chain (if any) for this request
  119. if (filterChain != null) {
  120. if (request.isComet()) {
  121. // If this is a Comet request, then the same chain will be used for the
  122. // processing of all subsequent events.
  123. filterChain.reuse();
  124. } else {
  125. //444444444 释放过滤器链
  126. filterChain.release();
  127. }
  128. }
  129. // Deallocate the allocated servlet instance
  130. //
  131. try {
  132. if (servlet != null) {
  133. //55555555555 释放 sevlet 实例
  134. wrapper.deallocate(servlet);
  135. }
  136. } catch (Throwable e) {
  137. //异常处理 略
  138. }
  139. // If this servlet has been marked permanently unavailable,
  140. // unload it and release this instance
  141. try {
  142. if ((servlet != null) &&
  143. (wrapper.getAvailable() == Long.MAX_VALUE)) {
  144. / /666666666666 卸载wrapper
  145. wrapper.unload();
  146. }
  147. } catch (Throwable e) {
  148. //异常处理 略
  149. }
  150. long t2=System.currentTimeMillis();
  151. long time=t2-t1;
  152. processingTime += time;
  153. if( time > maxTime) maxTime=time;
  154. if( time < minTime) minTime=time;
  155. }

好了,我们终于看到了最终去处理requestresponse代码的地方,虽然代码很长,但是思路很清楚,大多数代码都是在做检测判断等,invoke()方法我总结了核心是做了以下几件事,我已经在代码中注释出来了:

  1. 加载最终处理请求requestservlet实例
  2. 创建过滤器链(filterChain)
  3. 调用过滤器链的doFilter方法来处理对应的requestresponse
  4. 后续处理释放过滤器链
  5. 后续处理卸载该次处理的servlet实例
  6. 后续处理查看是否需要卸载对应的wrapper实例

个人总结出该方法做的比较重要的6件事,关于后续处理的部分我们就不查看了,有兴趣的可以自行查看,我们主要看处理过程,也就是123三条。

加载对应的Servlet

对应的方法wrapper.allocate():

  1. //代码清单8
  2. @Override
  3. public Servlet allocate() throws ServletException {
  4. // If we are currently unloading this servlet, throw an exception
  5. if (unloading) {
  6. throw new ServletException(sm.getString("standardWrapper.unloading", getName()));
  7. }
  8. boolean newInstance = false;
  9. // If not SingleThreadedModel, return the same instance every time
  10. //111 判断servlet是否是STM模式,如果是从来没加载过的servlet 默认是非STM模式的
  11. if (!singleThreadModel) {
  12. // Load and initialize our instance if necessary
  13. if (instance == null || !instanceInitialized) {
  14. synchronized (this) {
  15. if (instance == null) {
  16. try {
  17. if (log.isDebugEnabled()) {
  18. log.debug("Allocating non-STM instance");
  19. }
  20. // Note: We don't know if the Servlet implements
  21. // SingleThreadModel until we have loaded it.
  22. //22222222 加载servlet
  23. instance = loadServlet();
  24. newInstance = true;
  25. if (!singleThreadModel) {
  26. // For non-STM, increment here to prevent a race
  27. // condition with unload. Bug 43683, test case
  28. // #3
  29. countAllocated.incrementAndGet();
  30. }
  31. } catch (ServletException e) {
  32. throw e;
  33. } catch (Throwable e) {
  34. ExceptionUtils.handleThrowable(e);
  35. throw new ServletException(sm.getString("standardWrapper.allocate"), e);
  36. }
  37. }
  38. //3333 如果没有初始化 初始化
  39. if (!instanceInitialized) {
  40. initServlet(instance);
  41. }
  42. }
  43. }
  44. //44444 如果是STM模式的并且是分配的新对象 将该对象压入pool中
  45. //之所以在 非STM模式的判断里面又加入了STM模式判断是因为
  46. // 没有加载过的Servlet默认是非STM模式的,在loadServlet的时候回去判断 该Servlet是否 是STM模式的
  47. if (singleThreadModel) {
  48. if (newInstance) {
  49. // Have to do this outside of the sync above to prevent a
  50. // possible deadlock
  51. synchronized (instancePool) {
  52. instancePool.push(instance);
  53. nInstances++;
  54. }
  55. }
  56. } else {
  57. if (log.isTraceEnabled()) {
  58. log.trace(" Returning non-STM instance");
  59. }
  60. // For new instances, count will have been incremented at the
  61. // time of creation
  62. if (!newInstance) {
  63. countAllocated.incrementAndGet();
  64. }
  65. return instance;
  66. }
  67. }
  68. synchronized (instancePool) {
  69. //countAllocated 分配的活跃实例数量,对于一个非STM servlet 即使返回的是同一个数量,该字段也会增加
  70. //nInstances 分配的STM模式的servlet数量
  71. //maxInstances 可以分配的STM模式的servlet数量上限 默认是20
  72. while (countAllocated.get() >= nInstances) {
  73. // Allocate a new instance if possible, or else wait
  74. if (nInstances < maxInstances) {
  75. try {
  76. instancePool.push(loadServlet());
  77. nInstances++;
  78. } catch (ServletException e) {
  79. throw e;
  80. } catch (Throwable e) {
  81. ExceptionUtils.handleThrowable(e);
  82. throw new ServletException(sm.getString("standardWrapper.allocate"), e);
  83. }
  84. } else {
  85. try {
  86. instancePool.wait();
  87. } catch (InterruptedException e) {
  88. // Ignore
  89. }
  90. }
  91. }
  92. if (log.isTraceEnabled()) {
  93. log.trace(" Returning allocated STM instance");
  94. }
  95. countAllocated.incrementAndGet();
  96. return instancePool.pop();
  97. }
  98. }

在讲解之前,我们先介绍个概念:STM

STM是SingleThreadModel类的缩写,SingleThreadModel类是一个标志类(类似Serializable)。在Servlet2.4的规范中有说明:所有的servlet都可以实现该类,实现了该类的servlet不会同时有2个线程在调用同一个实例的service()方法。注意,这个意思并不是实现了SingleThreadModel类就代表该servlet线程安全。tomcat这样处理主要是为了保证高性能而不是线程安全,真正的线程安全还是要service()方法中的代码自己去控制。

我们继续查看源码,可以看到为了兼容STM和非STM模式servlet的分配allocate()方法写的略显复杂。总体是先判断该servlt是否加载过,如果没有加载过那么就是走标注1调用loadServlet()方法加载对应需要处理request的servlet。也许会奇怪为什么加载完了会再判断该servlet是否是STM模式的,主要是因为在没有加载过的servlet是无法判断其是否是STM模式的,但是默认是非STM模式的,所以在加载完毕servlet以后需要再判断一下是否是STM模式的然后作相应的处理。至于后面的synchronized代码块的处理我们先不看,我们先看下比较重要的标注2的地方的loadServlet()方法的源码:

  1. //代码清单9
  2. /**
  3. * 加载一个servlet
  4. * @return
  5. * @throws ServletException
  6. */
  7. public synchronized Servlet loadServlet() throws ServletException {
  8. //判断servlet 状态
  9. if (unloading) {
  10. throw new ServletException(
  11. sm.getString("standardWrapper.unloading", getName()));
  12. }
  13. // Nothing to do if we already have an instance or an instance pool
  14. //如果不是stm模式并且instance非空,那么直接返回instance(之前已经加载过该类)
  15. if (!singleThreadModel && (instance != null))
  16. return instance;
  17. //获取输出流,记日志
  18. PrintStream out = System.out;
  19. if (swallowOutput) {
  20. SystemLogHandler.startCapture();
  21. }
  22. Servlet servlet;
  23. try {
  24. long t1 = System.currentTimeMillis();
  25. // Complain if no servlet class has been specified
  26. //检测
  27. if (servletClass == null) {
  28. unavailable(null);
  29. throw new ServletException
  30. (sm.getString("standardWrapper.notClass", getName()));
  31. }
  32. InstanceManager instanceManager = ((StandardContext)getParent()).getInstanceManager();
  33. try {
  34. //111111新建实例
  35. servlet = (Servlet) instanceManager.newInstance(servletClass);
  36. } catch (ClassCastException e) {
  37. //异常处理 略
  38. } catch (Throwable e) {
  39. //异常处理 略
  40. }
  41. //处理servlet3.0 注解 MultipartConfig 配置该servlet的一些属性(上传文件的注解,上传文件的一些属性)
  42. if (multipartConfigElement == null) {
  43. MultipartConfig annotation =
  44. servlet.getClass().getAnnotation(MultipartConfig.class);
  45. if (annotation != null) {
  46. multipartConfigElement =
  47. new MultipartConfigElement(annotation);
  48. }
  49. }
  50. //处理 ServletSecurity 注解
  51. processServletSecurityAnnotation(servlet.getClass());
  52. // Special handling for ContainerServlet instances
  53. if ((servlet instanceof ContainerServlet) &&
  54. (isContainerProvidedServlet(servletClass) ||
  55. ((Context) getParent()).getPrivileged() )) {
  56. ((ContainerServlet) servlet).setWrapper(this);
  57. }
  58. classLoadTime=(int) (System.currentTimeMillis() -t1);
  59. if (servlet instanceof SingleThreadModel) {
  60. //22222如果是STM模式,为了达到高性能 需要从缓存池中取对象 缓存池是个stack
  61. if (instancePool == null) {
  62. instancePool = new Stack<Servlet>();
  63. }
  64. singleThreadModel = true;
  65. }
  66. //333333初始化servlet 会调用自定义servlet的 init()方法
  67. initServlet(servlet);
  68. fireContainerEvent("load", this);
  69. loadTime=System.currentTimeMillis() -t1;
  70. } finally {
  71. if (swallowOutput) {
  72. String log = SystemLogHandler.stopCapture();
  73. if (log != null && log.length() > 0) {
  74. if (getServletContext() != null) {
  75. getServletContext().log(log);
  76. } else {
  77. out.println(log);
  78. }
  79. }
  80. }
  81. }
  82. return servlet;
  83. }

loadServlet()方法也很简单,主要就是标注123,标注1的地方是在新建servlet实例,标注2的地方是新建STM模式的servlet缓存池,标注3的地方是把新建的servlet实例初始化,值得注意的是在initServlet()方法里会调用servlet实例的init(),我们来查看下initServlet()方法:

  1. //代码清单10
  2. private synchronized void initServlet(Servlet servlet)
  3. throws ServletException {
  4. //已经初始化
  5. if (instanceInitialized && !singleThreadModel) return;
  6. // Call the initialization method of this servlet
  7. try {
  8. instanceSupport.fireInstanceEvent(InstanceEvent.BEFORE_INIT_EVENT,
  9. servlet);
  10. if( Globals.IS_SECURITY_ENABLED) {
  11. boolean success = false;
  12. try {
  13. Object[] args = new Object[] { facade };
  14. SecurityUtil.doAsPrivilege("init",
  15. servlet,
  16. classType,
  17. args);
  18. success = true;
  19. } finally {
  20. if (!success) {
  21. // destroy() will not be called, thus clear the reference now
  22. SecurityUtil.remove(servlet);
  23. }
  24. }
  25. } else {
  26. //11111 servlet 初始化后 会调用一次 init()方法,可以自己复写,也可以不复写
  27. servlet.init(facade);
  28. }
  29. instanceInitialized = true;
  30. //触发事件
  31. instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT,
  32. servlet);
  33. } catch (UnavailableException f) {
  34. //异常处理 略
  35. } catch (ServletException f) {
  36. //异常处理 略
  37. } catch (Throwable f) {
  38. //异常处理 略
  39. }
  40. }

可以看到在标注1的地方调用了servlet实例的init()方法,其实这个就是用户自定义servlet可以复写也可以不复写的init()方法,值得注意的是传递的对象StandardWrapperFacade的实例,这个类实现了ServletConfig类,同时包装了StandardWrapper,我个人理解是这里传递StandardWrapperFacade对象主要目的是为了把StandardWrapper对servlet开发人员隐藏,不允许servlet开发人员随意使用StandardWrapper,是为了安全着想。

说到这里我们看下代码清单8的最后一段代码

  1. //代码清单11
  2. synchronized (instancePool) {
  3. //countAllocated 分配的活跃实例数量,对于一个非STM servlet 即使返回的是同一个数量,该字段也会增加
  4. //nInstances 分配的STM模式的servlet数量
  5. //maxInstances 可以分配的STM模式的servlet数量上限 默认是20
  6. while (countAllocated.get() >= nInstances) {
  7. // Allocate a new instance if possible, or else wait
  8. if (nInstances < maxInstances) {
  9. try {
  10. instancePool.push(loadServlet());
  11. nInstances++;
  12. } catch (ServletException e) {
  13. throw e;
  14. } catch (Throwable e) {
  15. ExceptionUtils.handleThrowable(e);
  16. throw new ServletException(sm.getString("standardWrapper.allocate"), e);
  17. }
  18. } else {
  19. try {
  20. instancePool.wait();
  21. } catch (InterruptedException e) {
  22. // Ignore
  23. }
  24. }
  25. }
  26. if (log.isTraceEnabled()) {
  27. log.trace(" Returning allocated STM instance");
  28. }
  29. countAllocated.incrementAndGet();
  30. return instancePool.pop();
  31. }

在当前StandardWrapper分配的活跃实例数量大于STM的servlet分配的实例数量,并且分配的STM实例数量小于限定值(20)的时候会不停的实例化该STM模式的servlet并且塞到缓存池(instancePool)中。最后把缓存池中的栈顶对象弹出使用,也就是一开始实例化20个对象,每个请求弹出一个对象使用,这样主要是为了保持高性能,以及每个请求使用一个servlet对象。

看到这里代码清单7的servlet = wrapper.allocate()方法就看完了,主要作用是初始化需要被使用的servlet,我们继续看代码清单7的标注23的内容。

FilterChain的创建以及调用

  1. //代码清单12
  2. //2222222 创建filterChain
  3. ApplicationFilterFactory factory = ApplicationFilterFactory.getInstance();
  4. ApplicationFilterChain filterChain = factory.createFilterChain(request, wrapper, servlet);
  5. filterChain.doFilter(request.getRequest(), response.getResponse());

先查看createFilterChain()方法:

  1. //代码清单13
  2. public ApplicationFilterChain createFilterChain(ServletRequest request, Wrapper wrapper, Servlet servlet) {
  3. //
  4. boolean comet = false;
  5. // Create and initialize a filter chain object
  6. ApplicationFilterChain filterChain = null;
  7. if (request instanceof Request) {
  8. Request req = (Request) request;
  9. comet = req.isComet();
  10. if (Globals.IS_SECURITY_ENABLED) {
  11. // Security: Do not recycle
  12. filterChain = new ApplicationFilterChain();
  13. if (comet) {
  14. req.setFilterChain(filterChain);
  15. }
  16. } else {
  17. filterChain = (ApplicationFilterChain) req.getFilterChain();
  18. if (filterChain == null) {
  19. //11111111 新建ApplicationFilterChain 实例
  20. filterChain = new ApplicationFilterChain();
  21. req.setFilterChain(filterChain);
  22. }
  23. }
  24. } else {
  25. // Request dispatcher in use
  26. filterChain = new ApplicationFilterChain();
  27. }
  28. filterChain.setServlet(servlet);
  29. filterChain.setSupport
  30. (((StandardWrapper)wrapper).getInstanceSupport());
  31. // Acquire the filter mappings for this Context
  32. StandardContext context = (StandardContext) wrapper.getParent();
  33. //22222 获取所有的filter
  34. FilterMap filterMaps[] = context.findFilterMaps();
  35. // If there are no filter mappings, we are done
  36. if ((filterMaps == null) || (filterMaps.length == 0))
  37. return (filterChain);
  38. // Acquire the information we will need to match filter mappings
  39. String servletName = wrapper.getName();
  40. // Add the relevant path-mapped filters to this filter chain
  41. //33333333 添加匹配servlet路径的filter
  42. for (int i = 0; i < filterMaps.length; i++) {
  43. if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
  44. continue;
  45. }
  46. if (!matchFiltersURL(filterMaps[i], requestPath))
  47. continue;
  48. //44444444 获取 filter对应的 ApplicationFilterConfig 对象
  49. ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
  50. context.findFilterConfig(filterMaps[i].getFilterName());
  51. if (filterConfig == null) {
  52. // FIXME - log configuration problem
  53. continue;
  54. }
  55. boolean isCometFilter = false;
  56. if (comet) {
  57. try {
  58. isCometFilter = filterConfig.getFilter() instanceof CometFilter;
  59. } catch (Exception e) {
  60. // Note: The try catch is there because getFilter has a lot of
  61. // declared exceptions. However, the filter is allocated much
  62. // earlier
  63. Throwable t = ExceptionUtils.unwrapInvocationTargetException(e);
  64. ExceptionUtils.handleThrowable(t);
  65. }
  66. if (isCometFilter) {
  67. filterChain.addFilter(filterConfig);
  68. }
  69. } else {
  70. // 5555555 添加filter
  71. filterChain.addFilter(filterConfig);
  72. }
  73. }
  74. // Add filters that match on servlet name second
  75. //666666666 添加匹配 servelt名字的filter
  76. for (int i = 0; i < filterMaps.length; i++) {
  77. if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
  78. continue;
  79. }
  80. if (!matchFiltersServlet(filterMaps[i], servletName))
  81. continue;
  82. ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
  83. context.findFilterConfig(filterMaps[i].getFilterName());
  84. if (filterConfig == null) {
  85. // FIXME - log configuration problem
  86. continue;
  87. }
  88. boolean isCometFilter = false;
  89. if (comet) {
  90. try {
  91. isCometFilter = filterConfig.getFilter() instanceof CometFilter;
  92. } catch (Exception e) {
  93. // Note: The try catch is there because getFilter has a lot of
  94. // declared exceptions. However, the filter is allocated much
  95. // earlier
  96. }
  97. if (isCometFilter) {
  98. filterChain.addFilter(filterConfig);
  99. }
  100. } else {
  101. filterChain.addFilter(filterConfig);
  102. }
  103. }
  104. // Return the completed filter chain、
  105. //最终返回 filterchain
  106. return (filterChain);
  107. }

代码其实很简单,注释我都在代码中添加了,先是创建ApplicationFilterChain实例,再向filterChain中添加和该servlet匹配的各种filter,主要这里需要解释一下filter体系里几个对象的关系。

818454-20170203234324589-225723454.jpg

  • FilterDef:代表一个filter,filter的定义类。类中的parameters变量存储了在初始化过滤器的时候需要的所有参数,参数解析在解析web.xml的时候进行添加。
  • ApplicationFilterConfig:实现FilterConfig接口,用于管理web应用第一次启动时创建的所有过滤器实例,简单理解就是用来管理filter类的统一管理类。
  • ApplicationFilterChain:代表一个过滤器链实体,请求在到达对应servlet之前会先经过该实例拥有的所有filter。

除了filter相关知识以外,代码清单13中context.findFilterMaps()表示了context对象和filter在启动的时候已经被关联在一起了,具体的关联代码前面说了一点,本文主要讲解的是请求流程的处理,所以这里具体代码就不查看了,只提一下。filter的初始化和关联context的代码都在context对象的初始化时进行,类似deploy项目一样的监听器HostConfig类,StandardContext类初始化的时候使用的监听器是ContextConfig,具体代码可以在该类中查找。

看完代码清单13我们看到了ApplicationFilterChain的创建过程,从创建过程中我们知道了创建出来的filterChain实例拥有对于该请求应该应用的所有filter的实例引用。我们继续查看doFilter()方法。

  1. //代码清单14
  2. @Override
  3. public void doFilter(ServletRequest request, ServletResponse response)
  4. throws IOException, ServletException {
  5. if( Globals.IS_SECURITY_ENABLED ) {
  6. final ServletRequest req = request;
  7. final ServletResponse res = response;
  8. try {
  9. java.security.AccessController.doPrivileged(
  10. new java.security.PrivilegedExceptionAction<Void>() {
  11. @Override
  12. public Void run()
  13. throws ServletException, IOException {
  14. internalDoFilter(req,res);
  15. return null;
  16. }
  17. }
  18. );
  19. } catch( PrivilegedActionException pe) {
  20. //异常处理略
  21. }
  22. } else {
  23. internalDoFilter(request,response);
  24. }
  25. }

最后调用的是internalDoFilter()方法:

  1. //代码清单15
  2. /**
  3. * The int which is used to maintain the current position
  4. * in the filter chain.
  5. * 当前正在调用的filter的编号
  6. */
  7. private int pos = 0;
  8. /**
  9. * The int which gives the current number of filters in the chain.
  10. * filter的总的数量
  11. */
  12. private int n = 0;
  13. private void internalDoFilter(ServletRequest request,
  14. ServletResponse response)
  15. throws IOException, ServletException {
  16. // Call the next filter if there is one
  17. if (pos < n) {
  18. //1111 获取ApplicationFilterConfig对象
  19. ApplicationFilterConfig filterConfig = filters[pos++];
  20. Filter filter = null;
  21. try {
  22. //2222222222222 获取对应的filter实例
  23. filter = filterConfig.getFilter();
  24. support.fireInstanceEvent(InstanceEvent.BEFORE_FILTER_EVENT,
  25. filter, request, response);
  26. if (request.isAsyncSupported() && "false".equalsIgnoreCase(
  27. filterConfig.getFilterDef().getAsyncSupported())) {
  28. request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,
  29. Boolean.FALSE);
  30. }
  31. if( Globals.IS_SECURITY_ENABLED ) {
  32. final ServletRequest req = request;
  33. final ServletResponse res = response;
  34. Principal principal =
  35. ((HttpServletRequest) req).getUserPrincipal();
  36. Object[] args = new Object[]{req, res, this};
  37. SecurityUtil.doAsPrivilege
  38. ("doFilter", filter, classType, args, principal);
  39. } else {
  40. //33333 调用该filter的`doFilter()`方法
  41. filter.doFilter(request, response, this);
  42. }
  43. support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
  44. filter, request, response);
  45. } catch (IOException e) {
  46. //异常处理略
  47. } catch (ServletException e) {
  48. //异常处理略
  49. } catch (RuntimeException e) {
  50. //异常处理略
  51. } catch (Throwable e) {
  52. //异常处理略
  53. }
  54. return;
  55. }
  56. // We fell off the end of the chain -- call the servlet instance
  57. // 所有的filter都调用完毕以后调用 对应的 servlet
  58. try {
  59. if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
  60. lastServicedRequest.set(request);
  61. lastServicedResponse.set(response);
  62. }
  63. support.fireInstanceEvent(InstanceEvent.BEFORE_SERVICE_EVENT,
  64. servlet, request, response);
  65. if (request.isAsyncSupported()
  66. && !support.getWrapper().isAsyncSupported()) {
  67. request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,
  68. Boolean.FALSE);
  69. }
  70. // Use potentially wrapped request from this point
  71. if ((request instanceof HttpServletRequest) &&
  72. (response instanceof HttpServletResponse)) {
  73. if( Globals.IS_SECURITY_ENABLED ) {
  74. final ServletRequest req = request;
  75. final ServletResponse res = response;
  76. Principal principal =
  77. ((HttpServletRequest) req).getUserPrincipal();
  78. Object[] args = new Object[]{req, res};
  79. SecurityUtil.doAsPrivilege("service",
  80. servlet,
  81. classTypeUsedInService,
  82. args,
  83. principal);
  84. } else {
  85. //444444 调用对应servlet的`service()`方法
  86. servlet.service(request, response);
  87. }
  88. } else {
  89. servlet.service(request, response);
  90. }
  91. support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
  92. servlet, request, response);
  93. } catch (IOException e) {
  94. //异常处理略
  95. } catch (ServletException e) {
  96. //异常处理略
  97. } catch (RuntimeException e) {
  98. //异常处理略
  99. } catch (Throwable e) {
  100. //异常处理略
  101. } finally {
  102. if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
  103. lastServicedRequest.set(null);
  104. lastServicedResponse.set(null);
  105. }
  106. }
  107. }

从代码清单15中我们可以看到,如果请求还在filter链中流转那么就会一直调用filter.dofilter()方法,可以把代码清单14和代码清单15理解为一个递归方法,如果没满足pos < n这个条件就会一直调用filter.dofilter()方法,我们先看一下正常一个filter的dofilter()方法:

  1. //代码清单16
  2. @Override
  3. public void doFilter(ServletRequest request, ServletResponse response,
  4. FilterChain chain) throws IOException, ServletException {
  5. //自定义代码略
  6. chain.doFilter(request, response);
  7. }

而在清单15标注3的地方传递的正是自身filterChain的实例,所以在filter中再调用chain.doFilter()方法,相当于又去调用代码清单14的代码了,这也是类似递归的地方。而pos < n这个条件表示的意思就是filter链中filter还没有调用完毕。当filter调用完毕就会去调用请求对应的servlet的service方法。

看到这里我们终于把代码清单7中提及的filterChain部分看完了,代码清单7中后续的处理就不一一查看了,同时这个也是相当于整个处理流程的完结,因为已经调用到了对应servlet的service()方法。

既然到最后了,我们来总结下tomcat是如何处理HTTP请求的:

Socket-->Http11ConnectionHandler-->Http11Processor-->CoyoteAdapter-->StandardEngineValve-->StandardHostValve-->StandardContextValve-->ApplicationFilterChain-->Servlet

其实用uml画个时序图比较好,但是实在太懒了,大家可以随便找个tomcat请求的时序图配图看文更清晰。

新年快乐(完)

转载于:https://www.cnblogs.com/coldridgeValley/p/6363881.html

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/爱喝兽奶帝天荒/article/detail/741657
推荐阅读
相关标签
  

闽ICP备14008679号