SpringMVC 请求调用流程源码解析

青旅半醒 2022-11-14 10:29 295阅读 0赞

前言

描述: springmvc相关文章如下:
【1】SpringMVC 入口及父子容器源码解析
【2】SpringMVC 请求调用流程源码解析

1 工程概述

在这里插入图片描述

1.1 pom

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <modelVersion>4.0.0</modelVersion>
  6. <packaging>war</packaging>
  7. <groupId>org.example</groupId>
  8. <artifactId>rosh-spring</artifactId>
  9. <version>1.0-SNAPSHOT</version>
  10. <properties>
  11. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  12. <maven.compiler.source>1.8</maven.compiler.source>
  13. <maven.compiler.target>1.8</maven.compiler.target>
  14. <spring.version>5.2.8.RELEASE</spring.version>
  15. </properties>
  16. <dependencies>
  17. <!--上下文-->
  18. <dependency>
  19. <groupId>org.springframework</groupId>
  20. <artifactId>spring-context</artifactId>
  21. <version>${ spring.version}</version>
  22. </dependency>
  23. <!--切面-->
  24. <dependency>
  25. <groupId>org.springframework</groupId>
  26. <artifactId>spring-aspects</artifactId>
  27. <version>${ spring.version}</version>
  28. </dependency>
  29. <!--事务-->
  30. <dependency>
  31. <groupId>org.springframework</groupId>
  32. <artifactId>spring-tx</artifactId>
  33. <version>${ spring.version}</version>
  34. </dependency>
  35. <!--spring-mvc-->
  36. <dependency>
  37. <groupId>org.springframework</groupId>
  38. <artifactId>spring-webmvc</artifactId>
  39. <version>${ spring.version}</version>
  40. </dependency>
  41. <!--jdbc-->
  42. <dependency>
  43. <groupId>org.springframework</groupId>
  44. <artifactId>spring-jdbc</artifactId>
  45. <version>${ spring.version}</version>
  46. </dependency>
  47. <dependency>
  48. <groupId>org.projectlombok</groupId>
  49. <artifactId>lombok</artifactId>
  50. <version>1.16.20</version>
  51. </dependency>
  52. <!--servlet-->
  53. <dependency>
  54. <groupId>javax.servlet</groupId>
  55. <artifactId>javax.servlet-api</artifactId>
  56. <version>3.1.0</version>
  57. <scope>provided</scope>
  58. </dependency>
  59. <!-- 日志相关依赖 -->
  60. <dependency>
  61. <groupId>org.slf4j</groupId>
  62. <artifactId>slf4j-api</artifactId>
  63. <version>1.7.10</version>
  64. </dependency>
  65. <dependency>
  66. <groupId>ch.qos.logback</groupId>
  67. <artifactId>logback-classic</artifactId>
  68. <version>1.1.2</version>
  69. </dependency>
  70. <dependency>
  71. <groupId>ch.qos.logback</groupId>
  72. <artifactId>logback-core</artifactId>
  73. <version>1.1.2</version>
  74. </dependency>
  75. <!--测试-->
  76. <dependency>
  77. <groupId>junit</groupId>
  78. <artifactId>junit</artifactId>
  79. <version>4.13</version>
  80. </dependency>
  81. </dependencies>
  82. </project>

1.2 配置文件

  1. public class RoshWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
  2. /** * 父容器配置文件 */
  3. @Override
  4. protected Class<?>[] getRootConfigClasses() {
  5. System.out.println("RoshWebInitializer invoke getRootConfigClasses");
  6. return new Class<?>[]{ SpringRootConfig.class};
  7. }
  8. /** * 子容器配置 */
  9. @Override
  10. protected Class<?>[] getServletConfigClasses() {
  11. System.out.println("RoshWebInitializer invoke getServletConfigClasses");
  12. return new Class<?>[]{ SpringServletConfig.class};
  13. }
  14. /** *拦截请求(静态资源、js、css.......) */
  15. @Override
  16. protected String[] getServletMappings() {
  17. return new String[]{ "/"};
  18. }
  19. }
  20. /** * 父容器配置文件,只扫描service */
  21. @Configuration
  22. @ComponentScan(value = "com.rosh.service")
  23. public class SpringRootConfig {
  24. }
  25. /** * 子容器配置文件,仅仅扫描@Controller、@RestController */
  26. @Configuration
  27. @ComponentScan(value="com.rosh",includeFilters={
  28. @ComponentScan.Filter(type= FilterType.ANNOTATION,classes={ Controller.class, RestController.class})
  29. },useDefaultFilters=false)
  30. @EnableWebMvc
  31. public class SpringServletConfig {
  32. }

1.3 service

  1. @RestController
  2. @RequestMapping("/hello")
  3. public class HelloController {
  4. @Autowired
  5. private HelloService helloService;
  6. @GetMapping("")
  7. public String printHello() {
  8. return helloService.printHello();
  9. }
  10. }

1.4 Controller

  1. @RestController
  2. @RequestMapping("/hello")
  3. public class HelloController {
  4. @Autowired
  5. private HelloService helloService;
  6. @GetMapping("")
  7. public String printHello() {
  8. return helloService.printHello();
  9. }
  10. }

1.5 启动tomcat

在这里插入图片描述

2 DispatcherServlet

概述: springmvc 核心类,在创建子容器时,创建DispatcherServlet,处理url请求、响应等。当web发起请求时,调用doDispatch方法。

  1. (1) 根据request获取相应的HandlerExecutionChain(包含handlerMethod、拦截器等)
  2. (2) 根据handlerMethod获取相应的handlerAdapter(handler方法是具体调用)
  3. (3) 前置过滤
  4. (4) handlerAdapter.handler()具体调用
  5. (5) 中置过滤

在这里插入图片描述

  1. protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
  2. HttpServletRequest processedRequest = request;
  3. HandlerExecutionChain mappedHandler = null;
  4. boolean multipartRequestParsed = false;
  5. /** * 异步管理 */
  6. WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
  7. try {
  8. ModelAndView mv = null;
  9. Exception dispatchException = null;
  10. try {
  11. /** * 文件上传 */
  12. processedRequest = checkMultipart(request);
  13. multipartRequestParsed = (processedRequest != request);
  14. /** * 【重点】 根据request 获取相应的HandlerExecutionChain */
  15. // Determine handler for the current request.
  16. mappedHandler = getHandler(processedRequest);
  17. if (mappedHandler == null) {
  18. noHandlerFound(processedRequest, response);
  19. return;
  20. }
  21. /** * 根据handler获取adapter */
  22. // Determine handler adapter for the current request.
  23. /** * 根据handlerMethod 获取相应的 handlerAdapter */
  24. HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
  25. // Process last-modified header, if supported by the handler.
  26. String method = request.getMethod();
  27. boolean isGet = "GET".equals(method);
  28. if (isGet || "HEAD".equals(method)) {
  29. long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
  30. if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
  31. return;
  32. }
  33. }
  34. /** * 前置过滤 */
  35. if (!mappedHandler.applyPreHandle(processedRequest, response)) {
  36. return;
  37. }
  38. // Actually invoke the handler.
  39. /** * 具体调用Controller 方法 */
  40. mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
  41. if (asyncManager.isConcurrentHandlingStarted()) {
  42. return;
  43. }
  44. applyDefaultViewName(processedRequest, mv);
  45. /** * 中置过滤 */
  46. mappedHandler.applyPostHandle(processedRequest, response, mv);
  47. }
  48. catch (Exception ex) {
  49. dispatchException = ex;
  50. }
  51. catch (Throwable err) {
  52. // As of 4.3, we're processing Errors thrown from handler methods as well,
  53. // making them available for @ExceptionHandler methods and other scenarios.
  54. dispatchException = new NestedServletException("Handler dispatch failed", err);
  55. }
  56. processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
  57. }
  58. catch (Exception ex) {
  59. triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
  60. }
  61. catch (Throwable err) {
  62. triggerAfterCompletion(processedRequest, response, mappedHandler,
  63. new NestedServletException("Handler processing failed", err));
  64. }
  65. finally {
  66. if (asyncManager.isConcurrentHandlingStarted()) {
  67. // Instead of postHandle and afterCompletion
  68. if (mappedHandler != null) {
  69. mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
  70. }
  71. }
  72. else {
  73. // Clean up any resources used by a multipart request.
  74. if (multipartRequestParsed) {
  75. cleanupMultipart(processedRequest);
  76. }
  77. }
  78. }
  79. }

3 mappedHandler 源码解析

  1. 概念:handlerMethod 具体处理方法
  2. requestMappingInfo @RequestMapping相关配置

在这里插入图片描述

3.1 过程解析

3.1.1 handlerMapping解析

描述: handlerMapping解析,获取相应的HandlerExecutionChain
在这里插入图片描述

3.1.2 获取handlerMethod

描述: 根据request获取handlerMethod
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
描述: 根据uri获取相应的hadnlerMethod,核心方法。

在这里插入图片描述

  1. @Nullable
  2. protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
  3. List<Match> matches = new ArrayList<>();
  4. /** * 根据url获取mapping匹配关系 */
  5. List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
  6. //如果mapping不为空
  7. if (directPathMatches != null) {
  8. /** * 根据mapping、request匹配相应的handlerMethod */
  9. addMatchingMappings(directPathMatches, matches, request);
  10. }
  11. if (matches.isEmpty()) {
  12. // No choice but to go through all mappings...
  13. addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
  14. }
  15. /** * 如果matches不为空 */
  16. if (!matches.isEmpty()) {
  17. //获取第一个方法
  18. Match bestMatch = matches.get(0);
  19. if (matches.size() > 1) {
  20. Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
  21. matches.sort(comparator);
  22. bestMatch = matches.get(0);
  23. if (logger.isTraceEnabled()) {
  24. logger.trace(matches.size() + " matching mappings: " + matches);
  25. }
  26. if (CorsUtils.isPreFlightRequest(request)) {
  27. return PREFLIGHT_AMBIGUOUS_MATCH;
  28. }
  29. /** * 如果匹配多个方法,获取第二个,并且第二个和第一个一样,那么报异常 */
  30. Match secondBestMatch = matches.get(1);
  31. if (comparator.compare(bestMatch, secondBestMatch) == 0) {
  32. Method m1 = bestMatch.handlerMethod.getMethod();
  33. Method m2 = secondBestMatch.handlerMethod.getMethod();
  34. String uri = request.getRequestURI();
  35. throw new IllegalStateException(
  36. "Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
  37. }
  38. }
  39. /** * request设置值 */
  40. request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
  41. handleMatch(bestMatch.mapping, lookupPath, request);
  42. return bestMatch.handlerMethod;
  43. }
  44. else {
  45. return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
  46. }
  47. }

在这里插入图片描述

3.2 初始化映射关系的建立

描述: 在匹配过程中,AbstractHandlerMethodMapping.lookupHandlerMethod方法,根据uri获取requestMappingInfo、在根据requestMappingInfo获取handlerMethod,那么映射关系是如何建立的呢?

afterPropertiesSet

在这里插入图片描述
描述: 遍历所有的beanNames
在这里插入图片描述
在这里插入图片描述
描述: 如果当前类有Controller、RequestMapping注解,那么遍历所有方法,并且用getMappingForMethod进行方法匹配,如果匹配成功,创建RequestMappingInfo对象。
在这里插入图片描述

描述: 点进去,遍历方法,调用匿名函数进行方法匹配。
在这里插入图片描述

描述: 匿名匹配源码解析,带有@RequestMapping注解的方法创建RequestMappingInfo对象,并且判断该类是否带有@RequestMapping,如果带有,那么进行合并,返回RequestMappingInfo对象。

在这里插入图片描述

  1. @Override
  2. @Nullable
  3. protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
  4. /** * 带有@RequestMapping注解方法,然后注解里面的内容封装成对象 */
  5. RequestMappingInfo info = createRequestMappingInfo(method);
  6. if (info != null) {
  7. /** * 类上面的@RequestMapping注解也封装成对象 */
  8. RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
  9. if (typeInfo != null) {
  10. /** * 把方法上面的注解属性结合到类上面的RequestMappingInfo对象中 */
  11. info = typeInfo.combine(info);
  12. }
  13. String prefix = getPathPrefix(handlerType);
  14. if (prefix != null) {
  15. info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info);
  16. }
  17. }
  18. return info;
  19. }

描述: 创建映射关系。
在这里插入图片描述
在这里插入图片描述

  1. public void register(T mapping, Object handler, Method method) {
  2. // Assert that the handler method is not a suspending one.
  3. if (KotlinDetector.isKotlinType(method.getDeclaringClass())) {
  4. Class<?>[] parameterTypes = method.getParameterTypes();
  5. if ((parameterTypes.length > 0) && "kotlin.coroutines.Continuation".equals(parameterTypes[parameterTypes.length - 1].getName())) {
  6. throw new IllegalStateException("Unsupported suspending handler method detected: " + method);
  7. }
  8. }
  9. this.readWriteLock.writeLock().lock();
  10. try {
  11. /** * 【1】 创建handlerMethod */
  12. HandlerMethod handlerMethod = createHandlerMethod(handler, method);
  13. /** * 【2】 验证唯一 */
  14. validateMethodMapping(handlerMethod, mapping);
  15. /** * 【3】创建映射,uri-> handlerMethod */
  16. this.mappingLookup.put(mapping, handlerMethod);
  17. List<String> directUrls = getDirectUrls(mapping);
  18. for (String url : directUrls) {
  19. /** * 【4】 建立的是url 和 requestMappingInfo的映射 */
  20. this.urlLookup.add(url, mapping);
  21. }
  22. String name = null;
  23. if (getNamingStrategy() != null) {
  24. name = getNamingStrategy().getName(handlerMethod, mapping);
  25. addMappingName(name, handlerMethod);
  26. }
  27. /** * 【5】 跨域 */
  28. CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
  29. if (corsConfig != null) {
  30. this.corsLookup.put(handlerMethod, corsConfig);
  31. }
  32. this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
  33. }
  34. finally {
  35. this.readWriteLock.writeLock().unlock();
  36. }
  37. }

3.3 总结

(1) AbstractHandlerMethodMapping 在依赖注入完成后调用afterPropertiesSet方法,会建立url->requestMappingInfo->handlerMethod的映射关系。
(2) 当请求过来时,DispatcherServlet调用doDispatch方法处理,第一步根据handMapping获取相应的HandlerExecutionChain,其核心逻辑就是根据request获取requestMappingInfo,在根据requestMappingInfo获取handlerMethod最后封装成HandlerExecutionChain对象。

4 具体调用

在这里插入图片描述
在这里插入图片描述
描述: 调用赋值

  1. @Nullable
  2. protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
  3. HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
  4. ServletWebRequest webRequest = new ServletWebRequest(request, response);
  5. try {
  6. WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
  7. ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
  8. /** * 获取可调用的方法 */
  9. ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
  10. /** * 设置参数解析器 */
  11. if (this.argumentResolvers != null) {
  12. invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
  13. }
  14. /** * 设置返回值解析器 */
  15. if (this.returnValueHandlers != null) {
  16. invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
  17. }
  18. /** * 设置参数绑定工厂 */
  19. invocableMethod.setDataBinderFactory(binderFactory);
  20. /** * 设置参数名称解析类 */
  21. invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
  22. ModelAndViewContainer mavContainer = new ModelAndViewContainer();
  23. mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
  24. modelFactory.initModel(webRequest, mavContainer, invocableMethod);
  25. mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
  26. AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
  27. asyncWebRequest.setTimeout(this.asyncRequestTimeout);
  28. /** * 异步 */
  29. WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
  30. asyncManager.setTaskExecutor(this.taskExecutor);
  31. asyncManager.setAsyncWebRequest(asyncWebRequest);
  32. asyncManager.registerCallableInterceptors(this.callableInterceptors);
  33. asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
  34. if (asyncManager.hasConcurrentResult()) {
  35. Object result = asyncManager.getConcurrentResult();
  36. mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
  37. asyncManager.clearConcurrentResult();
  38. LogFormatUtils.traceDebug(logger, traceOn -> {
  39. String formatted = LogFormatUtils.formatValue(result, !traceOn);
  40. return "Resume with async result [" + formatted + "]";
  41. });
  42. invocableMethod = invocableMethod.wrapConcurrentResult(result);
  43. }
  44. /** * 【重点】方法调用 */
  45. invocableMethod.invokeAndHandle(webRequest, mavContainer);
  46. if (asyncManager.isConcurrentHandlingStarted()) {
  47. return null;
  48. }
  49. return getModelAndView(mavContainer, modelFactory, webRequest);
  50. }
  51. finally {
  52. webRequest.requestCompleted();
  53. }
  54. }

描述: 核心调用方法
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

发表评论

表情:
评论列表 (有 0 条评论,295人围观)

还没有评论,来说两句吧...

相关阅读

    相关 SpringMVC

    首先上时序图,帮助理解整个解析过程和执行过程 ![SpringMVC启动过程及执行请求过程][SpringMVC] 准备 Spring版本:5.0.8 解

    相关 SpringMVC请求过程

    对SpringMVC从请求到最后响应输出的过程结合源码进行了一个总结梳理,其中对于Spring底层代码理解还不到位,难免有不准确的地方,欢迎指正,结合源码梳理整个流程的流转图: