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);
}
}