缺省配置Spring boot Web中一个请求的处理流程
2018-01-21 18:33
567 查看
概述
缺省配置Spring boot Web中一个请求的处理流程,主要经历了以下组件,本文将基于源代码对此流程做详细分析 :Tomcat (容器标准组件)
Spring MVC (容器标准组件)
开发人员提供的Web Controller方法 (开发人员实现的业务逻辑)
本文用于分析该处理流程的源代码来自项目 :
完全采用缺省配置的最简Spring-boot Web应用
Tomcat 对请求的处理 ApplicationFilterChain
Tomcat servlet容器提供的通用逻辑,具体请参考 :Tomcat 的 ApplicationFilterChain
在Tomcat ApplicationFilterChain 处理一个请求时,最终对目标Servlet的调用,是调用其方法 :
final ServletRequest req = request; final ServletResponse res = response; // ... 省略部分 servlet.service(request, response);
Spring MVC 对请求的处理 DispatcherServlet
在缺省配置Spring boot Web应用中,Tomcat ApplicationFilterChain所要调用的目标Servlet,就是Spring MVC的DispatcherServlet 。在Spring MVC中,DispatcherServlet的继承关系如下 :
DispatcherServlet -> FrameworkServlet -> HttpServletBean -> HttpServlet -> GenericServlet
这里面,HttpServlet,GenericServlet 都是由Servlet API规范提供,其中 GenericServlet 实现了标准定义的Servlet接口。
DispatcherServlet,FrameworkServlet ,HttpServletBean 由Spring MVC提供。
上面提到的Tomcat ApplicationFilterChain所要调用的servlet.service()方法,由 FrameworkServlet 提供实现 :
@Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpMethod httpMethod = HttpMethod.resolve(request.getMethod()); if (HttpMethod.PATCH == httpMethod || httpMethod == null) { processRequest(request, response); } else { // 这里调用父类 HttpServlet 的方法 service(), // 父类 HttpServlet 的方法 service() 中又根据HTTP method的不同去调用 // doPost,doGet 等方法,而这些方法的最终实现也由 FrameworkServlet 提供, // 而FrameworkServlet的这些实现方法最终又调用FrameworkServlet的方法 // processRequest super.service(request, response); } }
下面是FrameworkServlet的processRequest方法 :
protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { long startTime = System.currentTimeMillis(); Throwable failureCause = null; // 记录方法进入时的当前请求处理线程的本地化上下文和请求上下文信息,在该方法结束时还要恢复到这些状态 LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext(); // 从方法参数request中重新构建本地化上下文对象,该方法逻辑会使用该对象 LocaleContext localeContext = buildLocaleContext(request); // 使用RequestContextHolder,从当前请求处理线程中获取记录的当前请求属性 RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes(); // 从方法参数request/response中重新构建请求属性对象,该方法逻辑会使用该对象 ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes); WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor()); // 将新构建的本地化上下文对象,请求属性上下文对象设置到线程上下文持有器中 : // localeContext -> LocaleContextHolder // requestAttributes -> RequestContextHolder initContextHolders(request, localeContext, requestAttributes); try { // 以上上下文设置就绪后,执行对请求的处理逻辑,该方法由 FrameworkServlet 定义, // 但 FrameworkServlet 并不提供实现,它的实现在 DispatcherServlet 中 doService(request, response); } catch (ServletException ex) { failureCause = ex; throw ex; } catch (IOException ex) { failureCause = ex; throw ex; } catch (Throwable ex) { failureCause = ex; throw new NestedServletException("Request processing failed", ex); } finally { // 请求处理完成,复位当前线程的上下文持有器的内容为方法进入时的状态 : // previousLocaleContext -> LocaleContextHolder // previousAttributes -> RequestContextHolder resetContextHolders(request, previousLocaleContext, previousAttributes); if (requestAttributes != null) { requestAttributes.requestCompleted(); } if (logger.isDebugEnabled()) { if (failureCause != null) { 4000 this.logger.debug("Could not complete request", failureCause); } else { if (asyncManager.isConcurrentHandlingStarted()) { logger.debug("Leaving response open for concurrent processing"); } else { this.logger.debug("Successfully completed request"); } } } // 发布请求处理完成事件 publishRequestHandledEvent(request, response, startTime, failureCause); } }
现在来看 DispatcherServlet 中方法 doService() 的实现 :
/** * Exposes the DispatcherServlet-specific request attributes and delegates to doDispatch * for the actual dispatching. * 该方法将最终的业务处理交给了doDispatch()方法。而该方法本身做的主要工作就是设置doDispatch()工作 * 所需的各种 request 属性,然后将工作委托给 doDispatch() 方法去做。 */ @Override protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { if (logger.isDebugEnabled()) { String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : ""; logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed + " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]"); } // Keep a snapshot of the request attributes in case of an include, // to be able to restore the original attributes after the include. Map<String, Object> attributesSnapshot = null; if (WebUtils.isIncludeRequest(request)) { attributesSnapshot = new HashMap<String, Object>(); Enumeration<?> attrNames = request.getAttributeNames(); while (attrNames.hasMoreElements()) { String attrName = (String) attrNames.nextElement(); if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) { attributesSnapshot.put(attrName, request.getAttribute(attrName)); } } } // Make framework objects available to handlers and view objects. request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext()); request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver); request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver); request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource()); FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response); if (inputFlashMap != null) { request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap)); } request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap()); request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager); try { doDispatch(request, response); } finally { if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { // Restore the original attribute snapshot, in case of an include. if (attributesSnapshot != null) { restoreAttributesAfterInclude(request, attributesSnapshot); } } } }
然后我们来看doDispatch()方法都在做些什么事情 :
/** * Process the actual dispatching to the handler. 将请求派发到相应的处理器handler * The handler will be obtained by applying the servlet's HandlerMappings in order. * 处理器handler通过按顺序应用servlet的HandlerMappings获得。 * The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters * to find the first that supports the handler class. * 通过查询servlet上安装的HandlerAdapter,第一个支持该处理器handler类的HandlerAdapter会被使用。 * All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers * themselves to decide which methods are acceptable. * 该方法会处理所有的HTTP方法,但某个HTTP方法最终是否被接受由HandlerAdapter或者handler自己来决定。 * @param request current HTTP request * @param response current HTTP response * @throws Exception in case of any kind of processing failure */ protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; boolean multipartRequestParsed = false; WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try { ModelAndView mv = null; Exception dispatchException = null; try { // 是否是文件上传请求,如果是,会将参数request封装为另外一个 // MultipartHttpServletRequest对象request记到processedRequest processedRequest = checkMultipart(request); // 如果发现processedRequest和参数request对象不是同一个对象, // 说明当前处于一个文件上传请求中 multipartRequestParsed = (processedRequest != request); // Determine handler for the current request. // 决定当前请求的 HandlerExecutionChain mappedHandler = getHandler(processedRequest); if (mappedHandler == null || mappedHandler.getHandler() == null) { // 针对当前请求,如果没有找到HandlerExecutionChain, // 则抛出异常NoHandlerFoundException或者向浏览器端返回响应 404 noHandlerFound(processedRequest, response); return; } // Determine handler adapter for the current request. // 决定当前请求的 HandlerAdapter , 可以将一个 HandlerAdapter 理解成 // 它对应一个开发人员实现的 Controller 方法 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // Process last-modified header, if supported by the handler. String method = request.getMethod(); boolean isGet = "GET".equals(method); if (isGet || "HEAD".equals(method)) { long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); if (logger.isDebugEnabled()) { logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified); } if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { return; } } // 在目标HandlerAdapter处理请求之前,调用HandlerExecutionChain中各个 // HandlerInterceptor的preHandle if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } // Actually invoke the handler. // 调用目标HandlerAdapter,最终是一个开发人员提供的Web Controller方法, // 返回结果是一个 ModelAndView 对象,View处理器会使用该对象渲染最终结果到 response mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return; } // 当 ModelAndView 对象存在但是没有指定view属性时,采用缺省view处理响应结果 applyDefaultViewName(processedRequest, mv); // 在目标HandlerAdapter处理请求之后,调用HandlerExecutionChain中各个 // HandlerInterceptor的postHandle mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception ex) { dispatchException = ex; } catch (Throwable err) { // As of 4.3, we're processing Errors thrown from handler methods as well, // making them available for @ExceptionHandler methods and other scenarios. dispatchException = new NestedServletException("Handler dispatch failed", err); } // 处理分发给HandlerAdapter,也就是目标Controller方法的请求的处理结果,这里面主要包含两部分信息 : // 1. ModelAndView view -- 控制器方法成功处理完请求后返回的对象 // 2. Exception dispatchException -- 控制器方法处理遇到异常时的异常信息对象 // 该方法主要做以下事情 : // 1. 如果有异常,处理异常 // 2. 如果没有异常,根据 ModelAndView ,结合view模板和model数据渲染响应结果 // 3. 调用HandlerExecutionChain中各个HandlerInterceptor的方法 afterCompletion processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } catch (Exception ex) { triggerAfterCompletion(processedRequest, response, mappedHandler, ex); } catch (Throwable err) { triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", err)); } finally { if (asyncManager.isConcurrentHandlingStarted()) { // Instead of postHandle and afterCompletion if (mappedHandler != null) { mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); } } else { // Clean up any resources used by a multipart request. if (multipartRequestParsed) { // 如果这是一个文件上传的请求,请求已经处理完了,对相应的数据做清理工作 cleanupMultipart(processedRequest); } } } }
开发人员提供的Web Controller 方法
Web Controller是留给开发人员用于实现业务逻辑的地方,对于一个用户请求的核心业务处理逻辑由开发人员实现并配置到容器,具体来讲,在Spring MVC应用中就是通过某种方式配置到DispatcherServlet,然后DispatcherServlet会在请求到达时,找到相应的Web Controller并执行相应的业务逻辑。举例来讲,下面是一个简单的Web Controller业务逻辑 , 在该业务逻辑方法中,开发人员仅仅返回一个字符串到浏览器端 :
@Controller public class SampleController { @RequestMapping("/") @ResponseBody String home() { return "Hello World!"; } }
相关文章推荐
- Spring Boot中使用AOP统一处理Web请求日志
- Spring Boot :Request请求处理流程
- 46. Spring Boot中使用AOP统一处理Web请求日志【从零开始学Spring Boot】
- springboot【19】日志管理之使用AOP统一处理Web请求日志
- Spring Boot中使用AOP统一处理Web请求日志
- Spring Boot(十一)使用AOP,@Aspect统一处理Web请求日志
- SpringWeb MVC处理请求的流程:(处理器映射器、处理器适配器、视图解析器称为springmvc的三大组件)
- spring boot 源码解析56-actuator请求处理流程(以EnvironmentEndpoint为例)
- Spring Web MVC处理请求的流程
- Spring Web MVC处理请求流程
- (十四)SpringBoot使用AOP统一处理Web请求日志添加MDC
- Spring Web MVC处理请求的流程
- Spring Boot中使用AOP统一处理Web请求日志
- SpringBoot +maven +idea 最简单的一个web程序的演示流程
- JAVA温习:Spring Web 处理请求的流程 核心架构图
- @Aspect统一处理Web请求日志--Spring Boot--Java EE开发,原来可以这样!
- Spring Boot教程(六)使用AOP统一处理Web请求日志
- Spring Web MVC处理请求的流程
- Spring Boot中使用AOP统一处理Web请求日志
- Spring Boot中使用AOP统一处理Web请求日志