Spring MVC 消息解析源码
spring-mvc.xml 配置
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> <mvc:annotation-driven/> <!-- 扫描web包,应用Spring的注解 --> <context:component-scan base-package="cn.edu.cqvie.mvc.controller"/> <!-- jsp视图 --> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/> <property name="prefix" value="/WEB-INF/views/jsp/"/> <property name="suffix" value=".jsp"/> </bean> <!-- 静态资源不走controller --> <mvc:resources mapping="/resources/**" location="/static/"/> <!-- 配置消息转换器支持接受 application/json;charset=UTF-8 格式请求 --> <mvc:annotation-driven> <!--设置不使用默认的消息转换器--> <mvc:message-converters register-defaults="false"> <!--配置spring的转换器--> <bean class="org.springframework.http.converter.StringHttpMessageConverter"/> <bean class="org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter"/> <bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter"/> <bean class="org.springframework.http.converter.BufferedImageHttpMessageConverter"/> <!--配置fastjson中实现HttpMessageConverter接口的转换器--> <bean id="fastJsonHttpMessageConverter" class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter"> <!--加入支持的媒体类型,返回contentType--> <property name="supportedMediaTypes"> <list> <!--这里顺序不能反,一定要先写text/html,不然IE下会出现下载提示--> <value>text/html;charset=UTF-8</value> <value>application/json;charset=UTF-8</value> </list> </property> </bean> </mvc:message-converters> </mvc:annotation-driven> </beans>
Spring MVC 将参数进行反序列化过程
- Spring MVC 将参数进行反序列化的入口类是
AbstractMessageConverterMethodArgumentResolver
它主要是实现方法参数的解析
下面是我一个测试接口代码:
@Controller @RequestMapping("/api") public class TestController { @PostMapping("/test") @ResponseBody public TestDto test(@RequestBody TestDto dto) { return new TestDto(); } @GetMapping("/") public String test1() { return "hello spring mvc"; } }
- 我发起
/api/test
请求, 然后会进入AbstractMessageConverterMethodArgumentResolver
进行方法参数解析
- 上面的
this.messageConverters
就是我们配置的消息转换器,如果我们是 json 个格式将来到下面的代码。
if (converter.canRead(targetClass, contentType)) { // 判断是否能转换成功 if (logger.isDebugEnabled()) { logger.debug("Read [" + targetType + "] as \"" + contentType + "\" with [" + converter + "]"); } if (inputMessage.getBody() != null) { inputMessage = getAdvice().beforeBodyRead(inputMessage, parameter, targetType, converterType); body = ((HttpMessageConverter<T>) converter).read(targetClass, inputMessage); body = getAdvice().afterBodyRead(body, inputMessage, parameter, targetType, converterType); } else { body = getAdvice().handleEmptyBody(null, inputMessage, parameter, targetType, converterType); } break; } // FastJsonHttpMessageConverter 判断是否能解析方法 public boolean canRead(Type type, Class<?> contextClass, MediaType mediaType) { return super.canRead(contextClass, mediaType); } // AbstractHttpMessageConverter public boolean canRead(Class<?> clazz, MediaType mediaType) { return this.supports(clazz) && this.canRead(mediaType); } protected boolean canRead(MediaType mediaType) { if (mediaType == null) { return true; } else { Iterator var2 = this.getSupportedMediaTypes().iterator(); MediaType supportedMediaType; do { if (!var2.hasNext()) { return false; } supportedMediaType = (MediaType)var2.next(); } while(!supportedMediaType.includes(mediaType)); return true; } }
这里需要注意的是我们配置的消息转换器支持的消息格式 supportedMediaTypes
是我们在 spring-xml
中配置 <value>application/json;charset=UTF-8</value>
。 如果能解析成功就可以进入消息转换器,如果不能解析那么就继续遍历其它的消息转换器。
- 如果不能转换成功返回
HttpMediaTypeNotSupportedException
if (body == NO_VALUE) { if (httpMethod == null || !SUPPORTED_METHODS.contains(httpMethod) || (noContentType && inputMessage.getBody() == null)) { return null; } throw new HttpMediaTypeNotSupportedException(contentType, this.allSupportedMediaTypes); }
Spring MVC 请求处理
// HttpServlet protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String method = req.getMethod(); if (method.equals(METHOD_GET)) { long lastModified = getLastModified(req); if (lastModified == -1) { // servlet doesn't support if-modified-since, no reason // to go through further expensive logic doGet(req, resp); ... doPost ... doHead // FrameworkServlet doGet、doPost。。。实现统一调用processRequest protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } // processRequest的实现 protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { long startTime = System.currentTimeMillis(); Throwable failureCause = null; //previousLocaleContext获取和当前线程相关的LocaleContext,根据已有请求构造一个新的和当前线程相关的LocaleContext LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext(); LocaleContext localeContext = buildLocaleContext(request); //previousAttributes获取和当前线程绑定的RequestAttributes RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes(); //为已有请求构造新的ServletRequestAttributes,加入预绑定属性 ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes); WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);//异步请求处理 asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor()); //initContextHolders让新构造的RequestAttributes和ServletRequestAttributes和当前线程绑定,加入到ThreadLocal,完成绑定 initContextHolders(request, localeContext, requestAttributes); try { //抽象方法doService由FrameworkServlet子类DispatcherServlet重写 doService(request, response); }catch (ServletException | IOException ex) { failureCause = ex; throw ex; }catch (Throwable ex) { failureCause = ex; throw new NestedServletException("Request processing failed", ex); }finally { //解除RequestAttributes,ServletRequestAttributes和当前线程的绑定 resetContextHolders(request, previousLocaleContext, previousAttributes); if (requestAttributes != null) { requestAttributes.requestCompleted(); } logResult(request, response, failureCause, asyncManager); //注册监听事件ServletRequestHandledEvent,在调用上下文的时候产生Event publishRequestHandledEvent(request, response, startTime, failureCause); } }
// DispatcherServlet protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { logRequest(request); // Keep a snapshot of the request attributes in case of an include, // to be able to restore the original attributes after the include. Map<String, Object> attributesSnapshot = null; if (WebUtils.isIncludeRequest(request)) { attributesSnapshot = new HashMap<>();//保存request域中的数据,存一份快照 Enumeration<?> attrNames = request.getAttributeNames(); while (attrNames.hasMoreElements()) { String attrName = (String) attrNames.nextElement(); if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) { attributesSnapshot.put(attrName, request.getAttribute(attrName)); } } } //设置web应用上下文 request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext()); //国际化本地 request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver); //样式 request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver); //设置样式资源 request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource()); //请求刷新时保存属性 if (this.flashMapManager != null) { FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response); if (inputFlashMap != null) { request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap)); } //Flash attributes 在对请求的重定向生效之前被临时存储(通常是在session)中,并且在重定向之后被立即移除 request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap()); //FlashMap 被用来管理 flash attributes 而 FlashMapManager 则被用来存储,获取和管理 FlashMap 实体 request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager); } try { doDispatch(request, response);//核心方法 }finally { if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { // Restore the original attribute snapshot, in case of an include. if (attributesSnapshot != null) { restoreAttributesAfterInclude(request, attributesSnapshot);//将快照覆盖回去 } } } } 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 { //将request转换成multipartRequest,并检查是否解析成功(判断是否有文件上传) processedRequest = checkMultipart(request); multipartRequestParsed = (processedRequest != request); //根据请求信息获取handler(包含了拦截器) mappedHandler = getHandler(processedRequest); if (mappedHandler == null) { noHandlerFound(processedRequest, response); return; } //根据handler获取adapter 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; } //执行业务处理,返回视图模型 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return; } //给视图模型设置viewName 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); } //处理请求结果,使用了组件LocaleResolver, ViewResolver和ThemeResolver(view#render) 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); } } } }