spring mvc 会对request 参数以及response的输出作处理,这个很神奇,也很便捷。
HttpMessageConverter 是具体处理的相关接口,有若干实现类,如json,xml, text等等。
而HandlerAdapter 是SPRING MVC 最核心的对象,它的代码负责控制了这个转换的规则,具体来说,就是
1 根据具体 controller方法的注解@requestBody, @headerParam @pathParam等等,以及request的content-type, 选择合适的HttpMessageConverter,做参数的填充。
2 根据request的accept列表里面的参数,选择合适的HttpMessageConverter,做输出。
具体情况贴点AnnotationMethodHandlerAdapter的代码吧(流程为@RequestBody注解,其他类似):
入口是DispatcherServlet.doDispatch,调用了HandlerAdapter的handle方法. 。。。。然后到了下面这段代码。
public class AnnotationMethodHandlerAdapter extends WebContentGenerator implements HandlerAdapter, Ordered, BeanFactoryAware { 。。。。省略代码 有个内部私有类,继承了HandlerMethodInvoker private class ServletHandlerMethodInvoker extends HandlerMethodInvoker { 在request进来的时候,调用controller的具体方法执行之前,会调用一下HandlerMethodInvoker的invokeHandlerMethod代码: public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { 。。。省略 // Execute invokeHandlerMethod in synchronized block if required. if (this.synchronizeOnSession) { //这里可以配置基于session的锁。 HttpSession session = request.getSession(false); if (session != null) { Object mutex = WebUtils.getSessionMutex(session); synchronized (mutex) { return invokeHandlerMethod(request, response, handler); } } } return invokeHandlerMethod(request, response, handler); } protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { ServletHandlerMethodResolver methodResolver = getMethodResolver(handler); Method handlerMethod = methodResolver.resolveHandlerMethod(request); ServletHandlerMethodInvoker methodInvoker = new ServletHandlerMethodInvoker(methodResolver); ServletWebRequest webRequest = new ServletWebRequest(request, response); ExtendedModelMap implicitModel = new BindingAwareModelMap(); //这里会调用到controller 的方法,之前会解析并填充参数。 Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel); //这个填充了ModelAndView对象,并对response做处理,比如@responseBody ModelAndView mav = methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest); methodInvoker.updateModelAttributes(handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest); return mav; } public final Object invokeHandlerMethod(Method handlerMethod, Object handler, NativeWebRequest webRequest, ExtendedModelMap implicitModel){ //这个地方,找到了实际要执行的controller的那个方法,会通过反射调用 Method handlerMethodToInvoke = BridgeMethodResolver.findBridgedMethod(handlerMethod); try { 。。。省略了一些代码 //这里会填充方法的参数 Object[] args = resolveHandlerArguments(handlerMethodToInvoke, handler, webRequest, implicitModel); if (debug) { logger.debug("Invoking request handler method: " + handlerMethodToInvoke); } ReflectionUtils.makeAccessible(handlerMethodToInvoke); return handlerMethodToInvoke.invoke(handler, args); //这里实际调用controller } } //resolveHandlerArguments这个方法有点长, 就不贴了。这个方法,遍历了controller方法的参数,然后检查每个参数上的注解,根据注解的不同不去调用了readWithMessageConverters 这个方法。 readWithMessageConverters方法 大致是根据ContentType和messageConverters的配置,去把webReqeust里面拿到requestBody这个流对象,然后转换成 方法参数 private Object readWithMessageConverters(MethodParameter methodParam, HttpInputMessage inputMessage, Class paramType) throws Exception { //获得request的ContentType, 如果为空的话,会报错. MediaType contentType = inputMessage.getHeaders().getContentType(); if (contentType == null) { StringBuilder builder = new StringBuilder(ClassUtils.getShortName(methodParam.getParameterType())); String paramName = methodParam.getParameterName(); if (paramName != null) { builder.append(' '); builder.append(paramName); } throw new HttpMediaTypeNotSupportedException( "Cannot extract parameter (" + builder.toString() + "): no Content-Type found"); } //下面这段代码找到,相关的messageConverter,进行转换。 List<MediaType> allSupportedMediaTypes = new ArrayList<MediaType>(); if (this.messageConverters != null) { for (HttpMessageConverter<?> messageConverter : this.messageConverters) { allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes()); if (messageConverter.canRead(paramType, contentType)) { if (logger.isDebugEnabled()) { logger.debug("Reading [" + paramType.getName() + "] as \"" + contentType +"\" using [" + messageConverter + "]"); } return messageConverter.read(paramType, inputMessage); } } } //如果,我们不能处理这个ContentType,也需要报错。 throw new HttpMediaTypeNotSupportedException(contentType, allSupportedMediaTypes); }
AnnotationMethodHandlerAdapter如何处理@ResponseBody:
这是HandlerMethodInvoker的方法,返回对象是ModelAndView,如果controller的returnValue是httpEntity或则controller方法的签名是有@ResponseBody注解,则返回为空。如果为空,上层的ViewResolver就不处理了。 public ModelAndView (Method handlerMethodClass handlerTypeObject returnValue ) 如果是@ResponseBody,则会调用下面的方法: private void handleResponseBody(Object returnValue, ServletWebRequest webRequest) throws Exception { if (returnValue == null) { return; } HttpInputMessage inputMessage = createHttpInputMessage(webRequest); HttpOutputMessage outputMessage = createHttpOutputMessage(webRequest); writeWithMessageConverters(returnValue, inputMessage, outputMessage); } 这个和上面的那个read类似,是拿到Accept参数。去找到相应的messageConverter 做输出转换。 private void writeWithMessageConverters(Object returnValue, HttpInputMessage inputMessage, HttpOutputMessage outputMessage) throws IOException, HttpMediaTypeNotAcceptableException { List<MediaType> acceptedMediaTypes = inputMessage.getHeaders().getAccept(); if (acceptedMediaTypes.isEmpty()) { acceptedMediaTypes = Collections.singletonList(MediaType.ALL); } MediaType.sortByQualityValue(acceptedMediaTypes); Class<?> returnValueType = returnValue.getClass(); List<MediaType> allSupportedMediaTypes = new ArrayList<MediaType>(); if (getMessageConverters() != null) { for (MediaType acceptedMediaType : acceptedMediaTypes) { for (HttpMessageConverter messageConverter : getMessageConverters()) { if (messageConverter.canWrite(returnValueType, acceptedMediaType)) { messageConverter.write(returnValue, acceptedMediaType, outputMessage); if (logger.isDebugEnabled()) { MediaType contentType = outputMessage.getHeaders().getContentType(); if (contentType == null) { contentType = acceptedMediaType; } logger.debug("Written [" + returnValue + "] as \"" + contentType + "\" using [" + messageConverter + "]"); } this.responseArgumentUsed = true; return; } } } for (HttpMessageConverter messageConverter : messageConverters) { allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes()); } } throw new HttpMediaTypeNotAcceptableException(allSupportedMediaTypes); } }