0


【Spring】@RequestBody的实现原理

@RequestBody注解可以用于POST请求接收请求体中的参数,使用方式如下:

@Controller
public class IndexController {
    
    @PostMapping(value = "/submit", produces = MediaType.APPLICATION_JSON_VALUE)
    public void submit(@RequestBody UserInfo userInfo) {
        System.out.println(userInfo.toString());
    }
}

那么是如何从请求中解析数据设置到对应的参数中呢,接下来就从源码的角度一探究竟。

DispatcherServlet

是Spring MVC的核心,它对请求进行调度,收到请求后会进入

DispatcherServlet

doDispatch

方法中:

  1. 调用getHandler方法获取请求对应的Handler处理器;
  2. 根据handler获取对应的适配器,这里用到了适配器模式;
  3. 调用适配器的handle方法处理请求,它会返回一个ModelAndView对象;
public class DispatcherServlet extends FrameworkServlet {
    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 {
                // 检查是否有Multipart
                processedRequest = checkMultipart(request);
                multipartRequestParsed = (processedRequest != request);

                // 根据请求获取对应的处理器
                mappedHandler = getHandler(processedRequest);
                if (mappedHandler == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }

                // 根据handler获取对应的适配器
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

                // ...

                // 处理请求
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

                // ...
            }
            catch (Exception ex) {
                dispatchException = ex;
            }
            catch (Throwable err) {
                dispatchException = new NestedServletException("Handler dispatch failed", err);
            }
            processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
        }
        // ...
    }
}

通过POSTMAN模拟请求,在代码中打断点可以看到

HandlerAdapter

的类型为对

RequestMappingHandlerAdapter

:

handle

方法在其父类

AbstractHandlerMethodAdapter

中实现,在它的

handle

方法中,又调用了

handleInternal

方法处理请求,

handleInternal

是一个抽象方法,由具体的子类实现:

public abstract class AbstractHandlerMethodAdapter extends WebContentGenerator implements HandlerAdapter, Ordered {
    @Override
    @Nullable
    public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        // 处理请求
        return handleInternal(request, response, (HandlerMethod) handler);
    }

    @Nullable
    protected abstract ModelAndView handleInternal(HttpServletRequest request,
            HttpServletResponse response, HandlerMethod handlerMethod) throws Exception;
}

所以回到

RequestMappingHandlerAdapter

handleInternal

方法,里面调用了

invokeHandlerMethod

方法进行处理:

  1. 创建ServletInvocableHandlerMethod
  2. 调用invokeAndHandle方法继续请求处理;
  3. 调用getModelAndView方法返回ModelAndView;
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
        implements BeanFactoryAware, InitializingBean {
    @Override
    protected ModelAndView handleInternal(HttpServletRequest request,
            HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

        ModelAndView mav;
        checkRequest(request);
        if (this.synchronizeOnSession) {
            HttpSession session = request.getSession(false);
            if (session != null) {
                Object mutex = WebUtils.getSessionMutex(session);
                synchronized (mutex) {
                    // 执行请求
                    mav = invokeHandlerMethod(request, response, handlerMethod);
                }
            }
            else {
                // 执行请求
                mav = invokeHandlerMethod(request, response, handlerMethod);
            }
        }
        else {
            // 执行请求
            mav = invokeHandlerMethod(request, response, handlerMethod);
        }
        // ...
        return mav;
    }

    @Nullable
    protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
            HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

        ServletWebRequest webRequest = new ServletWebRequest(request, response);
        try {
            // ...
            // 创建ServletInvocableHandlerMethod
            ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
            // 调用invokeAndHandle方法处理请求
            invocableMethod.invokeAndHandle(webRequest, mavContainer);
            if (asyncManager.isConcurrentHandlingStarted()) {
                return null;
            }
            // 返回ModelAndView
            return getModelAndView(mavContainer, modelFactory, webRequest);
        }
        finally {
            webRequest.requestCompleted();
        }
    }
}
ServletInvocableHandlerMethod

invokeAndHandle

中调用了

invokeForRequest

方法执行请求,它的实现在其父类

InvocableHandlerMethod

中:

public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
    public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
            Object... providedArgs) throws Exception {
        // 执行请求
        Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
        setResponseStatus(webRequest);

        // ...
    }
}
invokeForRequest

中又调用了

getMethodArgumentValues

方法获取请求中的参数,处理逻辑如下:

  1. 调用getMethodParameters获取方法中的参数,也就是我们的请求处理器方法中的所有参数,上面看到submit只接收了一个UserInfo类型的参数,这里可以从断点中看到parameters中只有一个元素,类型为UserInfo:
  2. 对获取到方法中的所有参数进行遍历,通过处理器调用resolveArgument方法解析请求中的数据,解析每一个参数对应的值;
public class InvocableHandlerMethod extends HandlerMethod {
    @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));
        }
        return doInvoke(args);
    }
    
    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);
            // ...
            try {
                // 调用resolveArgument从请求中解析对应的数据
                args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
            }
            // ...
        }
        return args;
    }
}
resolveArgument

方法在

HandlerMethodArgumentResolverComposite

中实现:

  1. 调用getArgumentResolver方法获取对应的参数处理器resolver
  2. 调用resolverresolveArgument方法进行参数解析;
public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver {
    @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);
    }
}

从断点中可以看到此时的

resolver

RequestResponseBodyMethodProcessor

类型的:

进入到

RequestResponseBodyMethodProcessor

resolveArgument

方法中,它又调用了

readWithMessageConverters

方法解析参数,最终会进入到

AbstractMessageConverterMethodArgumentResolve

中的

readWithMessageConverters

方法:

public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
    @Override
    public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

        parameter = parameter.nestedIfOptional();
        // 通过转换器进行参数解析
        Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
        String name = Conventions.getVariableNameForParameter(parameter);
        // ...
        return adaptArgumentIfNecessary(arg, parameter);
    }

    @Override
    protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter,
            Type paramType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {

        HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
        Assert.state(servletRequest != null, "No HttpServletRequest");
        ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest);
        // 调用AbstractMessageConverterMethodArgumentResolver中readWithMessageConverters方法读取参数
        Object arg = readWithMessageConverters(inputMessage, parameter, paramType);
        if (arg == null && checkRequired(parameter)) {
            throw new HttpMessageNotReadableException("Required request body is missing: " +
                    parameter.getExecutable().toGenericString(), inputMessage);
        }
        return arg;
    }
}
readWithMessageConverters

方法处理逻辑如下:

  1. 遍历所有HTTP消息转换器,判断是否支持解析当前的请求参数类型;
  2. 如果转换器支持解析当前的参数类型并且有消息体内容,调用转换器的read方法进行解析;
public abstract class AbstractMessageConverterMethodArgumentResolver implements HandlerMethodArgumentResolver {
    @Nullable
    protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,
            Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {

        // ...
        try {
            message = new EmptyBodyCheckingHttpInputMessage(inputMessage);
            // 遍历所有的消息转换器
            for (HttpMessageConverter<?> converter : this.messageConverters) {
                Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
                GenericHttpMessageConverter<?> genericConverter =
                        (converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);
                // 判断是否支持当前参数类型的读取
                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);
                        // 调用read方法进行读取
                        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;
                }
            }
        }
        catch (IOException ex) {
            throw new HttpMessageNotReadableException("I/O error while reading input message", ex, inputMessage);
        }

        // ...
        return body;
    }
}

这里列举一些消息转换器的类型:

对于

application/json;charset=UTF-8

类型会进入到

MappingJackson2HttpMessageConverter

read

方法在其父类

AbstractJackson2HttpMessageConverter

,处理逻辑如下:

  1. 获取参数的Class类型,从断点中可以看出是[class com.example.demo.model.UserInfo];
  2. 调用readJavaType方法解析参数 (1)获取ContentType,前面可以看到请求接收的类型为application/json; (2)获取字符集,这里的字符集为UTF-8; (3)创建ObjectMapper对象,并从请求体中读取JSON数据,转为JAVA对象;
public abstract class AbstractJackson2HttpMessageConverter extends AbstractGenericHttpMessageConverter<Object> {

    @Override
    public Object read(Type type, @Nullable Class<?> contextClass, HttpInputMessage inputMessage)
            throws IOException, HttpMessageNotReadableException {
        // 获取参数的Class类型
        JavaType javaType = getJavaType(type, contextClass);
        // 解析参数
        return readJavaType(javaType, inputMessage);
    }

    private Object readJavaType(JavaType javaType, HttpInputMessage inputMessage) throws IOException {
        // 获取ContentType
        MediaType contentType = inputMessage.getHeaders().getContentType();
        // 获取字符集
        Charset charset = getCharset(contentType);
        ObjectMapper objectMapper = selectObjectMapper(javaType.getRawClass(), contentType);
        Assert.state(objectMapper != null, "No ObjectMapper for " + javaType);
        boolean isUnicode = ENCODINGS.containsKey(charset.name());
        try {
            // ...
            if (isUnicode) {
                // 获取HTTP请求体中的JSON数据,转为JAVA对象
                return objectMapper.readValue(inputMessage.getBody(), javaType);
            }
            else {
                Reader reader = new InputStreamReader(inputMessage.getBody(), charset);
                return objectMapper.readValue(reader, javaType);
            }
        }
        // ....
    }
}

到这里已经成功从HTTP请求体中的JSON数据,并转为JAVA对象,完成了参数的设置。

标签: android 服务器 linux

本文转载自: https://blog.csdn.net/Gefangenes/article/details/131869773
版权归原作者 野生的狒狒 所有, 如有侵权,请联系我们删除。

“【Spring】@RequestBody的实现原理”的评论:

还没有评论