springMVC @RequestBody 原理
上一篇说了请求处理流程,这里说说 springMVC 怎么处理参数的
springMVC 底层是靠 HttpMessageConvert 实现的,这个接口有很多实现类,不同的参数使用不同的消息转换器
源头还是 Servlet.service 开始,一步步跟到 DispatcherServlet.doDispatch 方法,然后到适配器执行 handler
-
适配器执行 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(); } }
-
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) { ... } }
-
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); }
-
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); } }
-
解析参数前要先确定用哪个解析器,这里看怎么确定解析器的
// 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); }
-
解析器确定了是 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; }
-
read 方法就不进去跟了,贴两个图,能证明是 jackson 处理就好了