资讯专栏INFORMATION COLUMN

SpringMVC之源码分析--请求过程

zxhaaa / 1185人阅读

摘要:概述根据规范,当用户请求到达应用时,由的方法进行处理,对于而言,处理用户请求的入口为,通过其父类的拦截处理。最后创建了群方便大家交流,可扫描加入,同时也可加我,共同学习共同进步,谢谢

概述

根据Servlet规范,当用户请求到达应用时,由Servlet的service()方法进行处理,对于Spring MVC而言,处理用户请求的入口为DispatcherServlet,通过其父类FrameworkServlet的service()拦截处理。

FrameworkServlet

用户请求的入口方法为service(request,response),源码如下:

</>复制代码

  1. /**
  2. * Override the parent class implementation in order to intercept PATCH requests.
  3. */
  4. @Override
  5. protected void service(HttpServletRequest request, HttpServletResponse response)
  6. throws ServletException, IOException {
  7. HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
  8. // Spring3.0增加了PATCH方法拦截处理
  9. if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
  10. processRequest(request, response);
  11. } else { // GET, HEAD, POST, PUT, DELETE, OPTIONS, TRACE
  12. super.service(request, response);
  13. }
  14. }

该方法重写了HttpServlet的service()方法,并增加了PATCH请求的拦截处理,其他诸如GET、POST、PUT等请求则调用父类的service方法,HttpServlet类的service()方法就是根据请求类型进行不同的路由处理,如为GET请求,则路由至doGet()方法处理,以此类推,此处省略源码。

假如用户请求为GET,则路由到子类(FrameworkServlet)的doGet()方法进行处理,doGet()源码如下:

</>复制代码

  1. @Override
  2. protected final void doGet(HttpServletRequest request, HttpServletResponse response)
  3. throws ServletException, IOException {
  4. processRequest(request, response);
  5. }

其实,所有类型的请求最终都由processRequest()方法进行处理,processRequest方法源码如下:

</>复制代码

  1. protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
  2. throws ServletException, IOException {
  3. long startTime = System.currentTimeMillis();
  4. Throwable failureCause = null;
  5. //
  6. LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
  7. // 构建请求(request)的LocalContext上下文,提供基本的作为当前地区的主要语言环境
  8. LocaleContext localeContext = buildLocaleContext(request);
  9. RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
  10. // 构建请求(request)的ServletRequestAttributes对象,保存本次请求的request和response
  11. ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
  12. // 异步请求处理的管理核心类
  13. WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
  14. asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
  15. // 绑定request context上线到当前线程
  16. initContextHolders(request, localeContext, requestAttributes);
  17. try {
  18. // 模板方法,调用子类(DispatcherServlet)的doService方法进行处理
  19. doService(request, response);
  20. }
  21. catch (ServletException | IOException ex) {
  22. failureCause = ex;
  23. throw ex;
  24. }
  25. catch (Throwable ex) {
  26. failureCause = ex;
  27. throw new NestedServletException("Request processing failed", ex);
  28. }
  29. finally {
  30. // 接触请求线程与LocalContext和RequestAttributes的绑定
  31. resetContextHolders(request, previousLocaleContext, previousAttributes);
  32. if (requestAttributes != null) {
  33. requestAttributes.requestCompleted();
  34. }
  35. if (logger.isDebugEnabled()) {
  36. if (failureCause != null) {
  37. this.logger.debug("Could not complete request", failureCause);
  38. }
  39. else {
  40. if (asyncManager.isConcurrentHandlingStarted()) {
  41. logger.debug("Leaving response open for concurrent processing");
  42. }
  43. else {
  44. this.logger.debug("Successfully completed request");
  45. }
  46. }
  47. }
  48. // 发布ApplicationEvent事件,可由ApplicationListener进行监听
  49. // 继承ApplicationListener接口,实现onApplicationEvent()接口,并注册到spring容器,即可捕获该事件
  50. publishRequestHandledEvent(request, response, startTime, failureCause);
  51. }
  52. }
DispatcherServlet

</>复制代码

  1. @Override
  2. protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
  3. if (logger.isDebugEnabled()) {
  4. String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
  5. logger.debug("DispatcherServlet with name "" + getServletName() + """ + resumed +
  6. " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
  7. }
  8. // 如果是include请求,保存request attribute快照数据,并在finally中进行还原
  9. Map attributesSnapshot = null;
  10. if (WebUtils.isIncludeRequest(request)) {
  11. attributesSnapshot = new HashMap<>();
  12. Enumeration attrNames = request.getAttributeNames();
  13. while (attrNames.hasMoreElements()) {
  14. String attrName = (String) attrNames.nextElement();
  15. if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
  16. attributesSnapshot.put(attrName, request.getAttribute(attrName));
  17. }
  18. }
  19. }
  20. // 把环境上下文设置到请求域中
  21. request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
  22. request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
  23. request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
  24. request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
  25. if (this.flashMapManager != null) {
  26. FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
  27. if (inputFlashMap != null) {
  28. request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
  29. }
  30. request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
  31. request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
  32. }
  33. try {
  34. // 调用请求处理方法
  35. doDispatch(request, response);
  36. }
  37. finally {
  38. if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
  39. // Restore the original attribute snapshot, in case of an include.
  40. if (attributesSnapshot != null) {
  41. restoreAttributesAfterInclude(request, attributesSnapshot);
  42. }
  43. }
  44. }
  45. }
  46. // 请求处理关键方法
  47. protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
  48. HttpServletRequest processedRequest = request;
  49. HandlerExecutionChain mappedHandler = null;
  50. boolean multipartRequestParsed = false;
  51. WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
  52. try {
  53. ModelAndView mv = null;
  54. Exception dispatchException = null;
  55. try {
  56. processedRequest = checkMultipart(request);
  57. multipartRequestParsed = (processedRequest != request);
  58. // Determine handler for the current request.
  59. mappedHandler = getHandler(processedRequest);
  60. if (mappedHandler == null) {
  61. noHandlerFound(processedRequest, response);
  62. return;
  63. }
  64. // Determine handler adapter for the current request.
  65. HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
  66. // Process last-modified header, if supported by the handler.
  67. String method = request.getMethod();
  68. boolean isGet = "GET".equals(method);
  69. if (isGet || "HEAD".equals(method)) {
  70. long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
  71. if (logger.isDebugEnabled()) {
  72. logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
  73. }
  74. if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
  75. return;
  76. }
  77. }
  78. if (!mappedHandler.applyPreHandle(processedRequest, response)) {
  79. return;
  80. }
  81. // Actually invoke the handler.
  82. mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
  83. if (asyncManager.isConcurrentHandlingStarted()) {
  84. return;
  85. }
  86. applyDefaultViewName(processedRequest, mv);
  87. mappedHandler.applyPostHandle(processedRequest, response, mv);
  88. }
  89. catch (Exception ex) {
  90. dispatchException = ex;
  91. }
  92. catch (Throwable err) {
  93. // As of 4.3, we"re processing Errors thrown from handler methods as well,
  94. // making them available for @ExceptionHandler methods and other scenarios.
  95. dispatchException = new NestedServletException("Handler dispatch failed", err);
  96. }
  97. processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
  98. }
  99. catch (Exception ex) {
  100. triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
  101. }
  102. catch (Throwable err) {
  103. triggerAfterCompletion(processedRequest, response, mappedHandler,
  104. new NestedServletException("Handler processing failed", err));
  105. }
  106. finally {
  107. if (asyncManager.isConcurrentHandlingStarted()) {
  108. // Instead of postHandle and afterCompletion
  109. if (mappedHandler != null) {
  110. mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
  111. }
  112. }
  113. else {
  114. // Clean up any resources used by a multipart request.
  115. if (multipartRequestParsed) {
  116. cleanupMultipart(processedRequest);
  117. }
  118. }
  119. }
  120. }

doDispatch()是Spring MVC中重要的方法,用户处理用户请求,其主要经过过程如下:

获取当前请求的Handler

获取当前请求的Handler Adapter

执行preHandle方法

执行Handle方法,即Controller中的方法

执行postHandle方法

处理返回结果

总结

本文简要概述了Spring MVC处理用户请求过程中所涉及的主要方法,对请求处理的流程做了概述,后续将对内部细节进行分析,以加深理解。

最后创建了qq群方便大家交流,可扫描加入,同时也可加我qq:276420284,共同学习、共同进步,谢谢!

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/71103.html

相关文章

  • SpringMVC源码分析--HandlerAdapter(二)

    摘要:概述本章我们主要分析处理组件的处理流程以及其接口源码。概括来说,使用组件分为两步,首先是注册组件,其次是处理用户请求,以下针对这两个过程进行详细的分析。本系列文章是基于。接下来的几章将分析提供的适配策略,希望本节对大家能有帮助,谢谢。 概述 本章我们主要分析Spring处理HandlerAdapter组件的处理流程以及其接口源码。概括来说,Spring使用HandlerAdapter组...

    Eastboat 评论0 收藏0
  • SpringMVC源码分析--HandlerMapping(六)

    摘要:概述上一节我们分析了的初始化过程,即创建并注册,本章我们分析下的请求处理过程,即查找。本系列文章是基于。最后创建了群方便大家交流,可扫描加入,同时也可加我,共同学习共同进步,谢谢 概述 上一节我们分析了RequestMappingHandlerMapping的初始化过程,即创建并注册HandlerMehtod,本章我们分析下RequestMappingHandlerMapping的请求...

    BDEEFE 评论0 收藏0
  • SpringMVC源码分析--LocaleResolver和ThemeResolver应用

    摘要:需求根据客户端环境,界面显示不同的国旗图案。选择的技术方案可利用提供的国际化和主题定制来解决。注意此时返回的中没有国际化及主题相关的信息。修改请求参数的值为荷兰,即后再发起请求,结果如下与预期一致,测试通过。 概述 以上分析了Spring MVC的LocaleResolver和ThemeResolver两个策略解析器,在实际项目中很少使用,尤其是ThemeResolver,花精力去分析...

    qpal 评论0 收藏0
  • SpringMVC源码分析--HandlerMapping(三)

    摘要:与类图对比,类继承自抽象类,其又继承自抽象类,再往上继承关系与一致。创建初始化上一章我们分析了的创建初始化过程,的创建初始化过程与一样,方法的入口在抽象类中的方法。至此,代码编写完毕。 概述 本节我们继续分析HandlerMapping另一个实现类BeanNameUrlHandlerMapping,从类的名字可知,该类会根据请求的url与spring容器中定义的bean的name属性值...

    fsmStudy 评论0 收藏0
  • SpringMVC源码分析--HandlerMapping(五)

    摘要:概述通过前三章的分析,我们简要分析了和,但对拦截器部分做详细的分析,拦截器的加载和初始化是三个相同的部分。 概述 通过前三章的分析,我们简要分析了SimpleUrlHandlerMapping、BeanNameUrlHandlerMapping和RequestMappingHandlerMapping,但对拦截器部分做详细的分析,拦截器的加载和初始化是三个HandlerMapping相...

    nanchen2251 评论0 收藏0

发表评论

0条评论

zxhaaa

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<