• SpringMVC源码分析 (2022.08.23)


    SpringBoot-SpringMVC源码分析 (2022.08.23)

    补充一波SpringMVC源码分析, 请求至响应分析 (版本: v5.3.6)

    核心组件:

    • 核心前端控制器: (用于拦截请求,获取url),
    • 处理器映射器:(用于找到需要处理的执行链或者方法参数),
    • 处理器适配器: (根据执行链用于找到合适的处理程序进行处理)
    • 处理器 与 拦截器: 用于执行执行链!
    • 请求参数解析器与类型转换器: 用于解析与转换请求参数绑定到处理器方法
    • 响应参数解析器与消息转换器: 用于解析与转换响应参数;
    • 视图: 一个接口可以支持返回不同页面,(html,jsp)
    • 视图解析器与渲染器: (用于解析与渲染返回的视图)
    • 异常解析器: 用于解决执行处理器出现的异常

    容器启动之初会初始化的组件

    处理器映射器

    会扫描创建好URL : 处理器, 封装进一个Map;

    入口类: RequestMappingHandlerMapping

    注册映射:

    当应用上下文创建该RequestMappingHandlerMapping 时候, 创建完Bean后会触发后续一些操作, 进行注册URL : 处理器 封装进一个Map中;

    // RequestMappingHandlerMapping.java
    public void afterPropertiesSet() {
    	// 映射一些配置
       this.config = new RequestMappingInfo.BuilderConfiguration();
       this.config.setTrailingSlashMatch(useTrailingSlashMatch());
       this.config.setContentNegotiationManager(getContentNegotiationManager());
    
       if (getPatternParser() != null) {
          this.config.setPatternParser(getPatternParser());
          Assert.isTrue(!this.useSuffixPatternMatch && !this.useRegisteredSuffixPatternMatch,
                "Suffix pattern matching not supported with PathPatternParser.");
       }
       else {
          this.config.setSuffixPatternMatch(useSuffixPatternMatch());
          this.config.setRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch());
          this.config.setPathMatcher(getPathMatcher());
       }
    	// 调用父类AbstractHandlerMethodMapping 的afterPropertiesSet方法
       super.afterPropertiesSet();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    // AbstractHandlerMethodMapping
    @Override
    public void afterPropertiesSet() {
       initHandlerMethods();
    }
    
    protected void initHandlerMethods() {
        // obtainApplicationContext().getBeanNamesForType(Object.class) 获取应用上下文所有注册的bean名称
    		for (String beanName : getCandidateBeanNames()) {
    			if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
    				processCandidateBean(beanName);
    			}
    		}
        // 所有URL => 处理器 映射完毕后调用, 获取到对应Map, 然后handlerMethodsInitialized 输出日志
    		handlerMethodsInitialized(getHandlerMethods());
    	}
    
    protected void processCandidateBean(String beanName) {
    		Class<?> beanType = null;
    		try {
                // 获取对应Bean类型
    			beanType = obtainApplicationContext().getType(beanName);
    		}
    		catch (Throwable ex) {
    			// An unresolvable bean type, probably from a lazy bean - let's ignore it.
    			if (logger.isTraceEnabled()) {
    				logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
    			}
    		}
        	// 判断是否是处理器
    		if (beanType != null && isHandler(beanType)) {
    			detectHandlerMethods(beanName);
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    // RequestMappingHandlerMapping.java
    protected boolean isHandler(Class<?> beanType) {
       return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
             AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    // AbstractHandlerMethodMapping
    protected void detectHandlerMethods(Object handler) {
        // 获取处理器类型
       Class<?> handlerType = (handler instanceof String ?
             obtainApplicationContext().getType((String) handler) : handler.getClass());
    
       if (handlerType != null) {
           // 获取定义类型, 防止获取到生成的子类
          Class<?> userType = ClassUtils.getUserClass(handlerType);
           // 
          Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
                (MethodIntrospector.MetadataLookup<T>) method -> {
                   try {
                       // 获取方法映射
                      return getMappingForMethod(method, userType);
                   }
                   catch (Throwable ex) {
                      throw new IllegalStateException("Invalid mapping on handler class [" +
                            userType.getName() + "]: " + method, ex);
                   }
                });
          if (logger.isTraceEnabled()) {
             logger.trace(formatMappings(userType, methods));
          }
          else if (mappingsLogger.isDebugEnabled()) {
             mappingsLogger.debug(formatMappings(userType, methods));
          }
          methods.forEach((method, mapping) -> {
             Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
             registerHandlerMethod(handler, invocableMethod, mapping);
          });
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    v618KJ.png

    处理器适配器

    入口 WebMvcConfigurationSupport # requestMappingHandlerAdapter()

    @Bean
    public RequestMappingHandlerAdapter requestMappingHandlerAdapter(
          @Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
          @Qualifier("mvcConversionService") FormattingConversionService conversionService,
          @Qualifier("mvcValidator") Validator validator) {
    	
       // 创建请求映射处理器适配器
       RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter();
       adapter.setContentNegotiationManager(contentNegotiationManager);
        // 设置消息转换器集合
       adapter.setMessageConverters(getMessageConverters());
        // 设置数据绑定
       adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer(conversionService, validator));
        // 设置自定义参数转换解析器
       adapter.setCustomArgumentResolvers(getArgumentResolvers());
        // 设置自定义返回值转换处理程序
       adapter.setCustomReturnValueHandlers(getReturnValueHandlers());
    
       if (jackson2Present) {
          adapter.setRequestBodyAdvice(Collections.singletonList(new JsonViewRequestBodyAdvice()));
          adapter.setResponseBodyAdvice(Collections.singletonList(new JsonViewResponseBodyAdvice()));
       }
    
       AsyncSupportConfigurer configurer = getAsyncSupportConfigurer();
       if (configurer.getTaskExecutor() != null) {
          adapter.setTaskExecutor(configurer.getTaskExecutor());
       }
       if (configurer.getTimeout() != null) {
          adapter.setAsyncRequestTimeout(configurer.getTimeout());
       }
       adapter.setCallableInterceptors(configurer.getCallableInterceptors());
       adapter.setDeferredResultInterceptors(configurer.getDeferredResultInterceptors());
    
       return adapter;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    设置Http消息转换器

    v6amVg.md.png

    设置参数转换和绑定器 (其他博客说明)

    v6IQ74.png

    设置参数解析器/返回参数处理器

    RequestBodyAdvice和ResponseBodyAdvice接口 (针对于@RequestBody@ResponseBody, 参数处理增强)

    非使用@RequestBody@ResponseBody, 参考 框架提供的 自定义实现一个参数解析器、响应参数处理 (扩展1.0有个简单例子)

    vc9bFI.png

    1

    适配器完结:

    v6au5j.png

    请求开始至响应流程

    请求是如何找到对应控制器类中对应方法, 转换参数进行执行的呢?

    入口类: org.springframework.web.servlet.DispatcherServlet # doService()

    doService() 方法只是设置了请求域中的一些属性, 然后会接着调用doDispatch(request, response) 这个方法才是真正的请求后续大量处理入口

    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
       HttpServletRequest processedRequest = request;
       HandlerExecutionChain mappedHandler = null;
       boolean multipartRequestParsed = false;
    	// 获取异步请求管理器 (https://www.cnblogs.com/deityjian/p/11503218.html)
       WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    
       try {
          ModelAndView mv = null;
          Exception dispatchException = null;
    
          try {
              // 检测是否是文件上传请求
             processedRequest = checkMultipart(request);
             multipartRequestParsed = (processedRequest != request);
    
             // Determine handler for the current request (根据请求获取映射处理器)
             mappedHandler = getHandler(processedRequest);
             if (mappedHandler == null) {
                noHandlerFound(processedRequest, response);
                return;
             }
    
             // Determine handler adapter for the current request. 通过处理器找适配器
             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 (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                   return;
                }
             }
    
             if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                return;
             }
    
             // Actually invoke the handler.
             mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    
             if (asyncManager.isConcurrentHandlingStarted()) {
                return;
             }
    
             applyDefaultViewName(processedRequest, mv);
             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);
          }
          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);
             }
          }
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82

    检测是否是文件上传

    v6GqII.png

    通过请求找处理器映射器

    mappedHandler = getHandler(processedRequest)

    vclYGT.png

    同时找到了处理器和执行链HandlerExecutionChain handler = mapping.getHandler(request)

    通过处理器找适配器并执行任务链

    HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()),

    vc3Ub9.png

    通过适配器执行处理器获取视图

    vcNPEQ.png

    获取到视图的后续处理

    vcNs2t.png

    到此整个请求处理过程的关键步骤都分析完了。理解了SpringMVC V5.x版本上中的请求处理流程;

    核心前端控制器执行异常情况

    vcUJij.png

    扩展

    1.0 自定义参数解析器( 到达controller前处理请求参数)

    甚至可以定义添加Converters参数转换器来实现

    @Configuration
    public class MyMVCConfig implements WebMvcConfigurer {
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(new HandlerMethodArgumentResolver() {
            // post请求 json交互可以使用实现Advice接口增加请求前参数处理
            // 非json交互 post, get请求, 方法参数实体,  参考 ServletModelAttributeMethodProcessor实现 
            // 非json交互 get 请求, 方法参数非实体  参考 RequestParamMethodArgumentResolver实现
            @Override
            public boolean supportsParameter(MethodParameter parameter) {
                return parameter.getParameter().getParameterizedType() == UserOne.class;
            }
    
            @Override
            public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
                Class parameterizedType = (Class) parameter.getParameter().getParameterizedType();
                Object target = parameterizedType.newInstance();
                WebDataBinder binder = binderFactory.createBinder(webRequest, target, parameter.getParameter().getName());
                ServletRequest servletRequest = webRequest.getNativeRequest(ServletRequest.class);
                ServletRequestDataBinder servletBinder = (ServletRequestDataBinder) binder;
                servletBinder.bind(servletRequest);
                return binder.getBindingResult().getTarget();
            }
        });
        WebMvcConfigurer.super.addArgumentResolvers(resolvers);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    2.0 自定义响应参数处理程序

    一般使用@ResponseBody 返回的使用Advice接口

    返回视图的参考: ViewNameMethodReturnValueHandler , 或者在源码HandlerMethodReturnValueHandlerComposite#handleReturnValue() 参考返回值使用的是那个, 参考着写

    1

  • 相关阅读:
    SpringCloud全系列知识(3)——Http客户端Feign
    [题解] Codeforces Global Round 22 1738 A B C D E F 题解
    【整理】HTTP相关版本对比
    图解分布式事务实现原理(一)
    利用LVM制作swap交换分区
    计算机毕业设计Django+Vue.js电影推荐系统 电影用户画像系统 电影可视化 电影大数据 机器学习 深度学习 知识图谱 Hadoop Spark
    mysql-8.0.31-macos12-x86_64记录
    C语言之scanf
    【开源AI平台】Determined调研
    说说mybatis中的#{} 和 ${}
  • 原文地址:https://blog.csdn.net/weixin_44600430/article/details/126492047