springMVC @RequestBody 原理

上一篇说了请求处理流程,这里说说 springMVC 怎么处理参数的

springMVC 底层是靠 HttpMessageConvert 实现的,这个接口有很多实现类,不同的参数使用不同的消息转换器

源头还是 Servlet.service 开始,一步步跟到 DispatcherServlet.doDispatch 方法,然后到适配器执行 handler

  1. 适配器执行 handler

    // org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#invokeHandlerMethod
    @Nullable
    protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
            HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
    
        ServletWebRequest webRequest = new ServletWebRequest(request, response);
        try {
            
            ...
            
            // 上面巴拉巴拉一大堆,这是关键方法
            invocableMethod.invokeAndHandle(webRequest, mavContainer);
            if (asyncManager.isConcurrentHandlingStarted()) {
                return null;
            }
    
            return getModelAndView(mavContainer, modelFactory, webRequest);
        }
        finally {
            webRequest.requestCompleted();
        }
    }
    
  2. org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod#invokeAndHandle

    public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
            Object... providedArgs) throws Exception {
    
        // 执行 handler,并且有了返回值
        Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
        setResponseStatus(webRequest);
    
        ...
            
        Assert.state(this.returnValueHandlers != null, "No return value handlers");
        try {
            // 这里在处理响应参数(后面说响应,这个流程先说请求参数的处理)
            this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
        }
        catch (Exception ex) {
            ...
        }
    }
    
  3. org.springframework.web.method.support.InvocableHandlerMethod#invokeForRequest

    @Nullable
    public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
            Object... providedArgs) throws Exception {
    	// 上一篇文章说这里在处理请求参数,现在看具体怎么处理的
        Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
        if (logger.isTraceEnabled()) {
            logger.trace("Arguments: " + Arrays.toString(args));
        }
        // 执行 handler,上一篇文章也说到这里,内部就是朴实无华的 jdk 反射调用方法而已
        return doInvoke(args);
    }
    
  4. org.springframework.web.method.support.InvocableHandlerMethod#getMethodArgumentValues

    protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
            Object... providedArgs) throws Exception {
    
        // 这个数组放的是每个请求参数
        MethodParameter[] parameters = getMethodParameters();
        if (ObjectUtils.isEmpty(parameters)) {
            return EMPTY_ARGS;
        }
    
        // 遍历参数
        Object[] args = new Object[parameters.length];
        for (int i = 0; i < parameters.length; i++) {
            MethodParameter parameter = parameters[i];
            parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
            args[i] = findProvidedArgument(parameter, providedArgs);
            if (args[i] != null) {
                continue;
            }
            // 如果所有的解析器都不能处理这种类型的参数抛异常(咋可能嘛,花里胡哨的参数都能解析,只有你想不出来的,没有 spring 没考虑到的)
            if (!this.resolvers.supportsParameter(parameter)) {
                throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
            }
            try {
                // 开始解析参数,内部也是遍历这个适配器的所有参数解析器,看哪个支持就用哪个
                args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
            }
            catch (Exception ex) {
                // Leave stack trace for later, exception may actually be resolved and handled...
                if (logger.isDebugEnabled()) {
                    String exMsg = ex.getMessage();
                    if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
                        logger.debug(formatArgumentError(parameter, exMsg));
                    }
                }
                throw ex;
            }
        }
        return args;
    }
    

    问题:解析器 resolvers 具体有哪些?怎么来的?

    初始化适配器的时候设置的,适配器对于 spring 来说也是一个 bean,也会遵循 bean 的生命周期,RequestMappingHandlerAdapter 实现了接口InitializingBean,所以会有个初始化的方法,就是下面这个

    @Override
    public void afterPropertiesSet() {
        // Do this first, it may add ResponseBody advice beans
        initControllerAdviceCache();
    
        // 请求参数解析器
        if (this.argumentResolvers == null) {
            // 如果参数为空,添加默认的解析器,getDefaultArgumentResolvers() 进去可以看到有 20 多种解析器
            // 每个参数解析器(HandlerMethodArgumentResolver)负责解析不同的参数,具体由方法 supportsParameter 体现
            // 每个解析器的 supportsParameter 方法就是看参数是否有特定注解,比如 @RequestParam、@PathVariable、@RequestBody 等
            List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
            this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
        }
        if (this.initBinderArgumentResolvers == null) {
            List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
            this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
        }
        // 响应解析器
        if (this.returnValueHandlers == null) {
            List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
            this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
        }
    }
    
  5. 解析参数前要先确定用哪个解析器,这里看怎么确定解析器的

    // org.springframework.web.method.support.HandlerMethodArgumentResolverComposite#resolveArgument
    @Override
    @Nullable
    public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
    
    	// 拿到解析器,就是遍历这个适配器的所有的解析器,看哪个合适
        HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
        if (resolver == null) {
            throw new IllegalArgumentException("Unsupported parameter type [" +
                    parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
        }
        // 确定了解析器,这里真正解析
        return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
    }
    
    // org.springframework.web.method.support.HandlerMethodArgumentResolverComposite#getArgumentResolver
    // 	怎么拿解析器
    @Nullable
    private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
        HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
        if (result == null) {
            for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
            	// 这里确定哪个解析器支持,内部就是根据当前参数的注解判断的
                if (resolver.supportsParameter(parameter)) {
                    result = resolver;
                    this.argumentResolverCache.put(parameter, result);
                    break;
                }
            }
        }
        return result;
    }
    

    RequestResponseBodyMethodProcessor 这个解析器处理参数标注了 @RequestBody 的参数

    // org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor#supportsParameter
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
    	// 参数是否有 RequestBody 注解
        return parameter.hasParameterAnnotation(RequestBody 注解.class);
    }
    
  6. 解析器确定了是 RequestResponseBodyMethodProcessor ,具体怎么解析

    // org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor#resolveArgument
    @Override
    public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
    
        parameter = parameter.nestedIfOptional();
        // 这里解析,arg 是解析后的,再进去看下源码
        Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
        ...
        return adaptArgumentIfNecessary(arg, parameter);
    }
    
    // 这个方法内容太多了,只看关键代码
    @Nullable
    protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,
            Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
    
        MediaType contentType;
        boolean noContentType = false;
        // 先得到 content-type
        try {
            contentType = inputMessage.getHeaders().getContentType();
        }
        
        ...
       
        try {
            message = new EmptyBodyCheckingHttpInputMessage(inputMessage);
    
            // 这里遍历 HttpMessageConvert,看哪个能处理
            for (HttpMessageConverter<?> converter : this.messageConverters) {
                Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
                GenericHttpMessageConverter<?> genericConverter =
                        (converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);
                // 每一个的判断,看是否能转型,同时也要检测 contentType(因为这里是 json 的请求,就是看这个对象是否能序列化转成json,同时 contenType 是否是 applicaiton/json)
                if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) :
                        (targetClass != null && converter.canRead(targetClass, contentType))) {
                    if (message.hasBody()) {
                        HttpInputMessage msgToUse =
                                getAdvice().beforeBodyRead(message, parameter, targetType, converterType);
                        // 这里真正转型(断点可以看到是 jackson 处理的)
                        body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :
                                ((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));
                        body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);
                    }
                    else {
                        body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);
                    }
                    break;
                }
            }
        }
    	...
        return body;
    }
    
  7. read 方法就不进去跟了,贴两个图,能证明是 jackson 处理就好了

posted @ 2024-07-03 13:41  CyrusHuang  阅读(3)  评论(0编辑  收藏  举报