springmvc的处理方法返回值处理
在springmvc我们自己创建的Controller中的方法,可以返回不同的返回值,而不管是什么样的返回值,最后的请求都会被正确的处理,在前面视图解析篇中,我们主要关注点是视图解析找视图,model数据合并,渲染视图,然而在这个过程中,我们忽略了这个解析视图的名称是怎么来的,resetFul请求(responseBody)没有视图,又是怎么中断请求的,不同的返回值是怎么通用的处理成modelAndValue的,带着这些问题,为了更加深入的探究这里面的奥秘,今天我们跟着源码,一起走进returnValue的小世界。
概述:
springmvc的Handler可以和请求参数一样,你可以设置成void或返回null,或者返回String,以及@ResponseBody注解标注的对象。
我们大部分人都知道当方法返回值为Null时,默认是将请求路径当做视图路径处理,String类型是解析返回值字符串, 当redirect:或forWord:会进行转发或重定向,否则就根据返回值String的路径找到对应的视图处理,如果是ResponseBody注解的对象类型,那么就会格式化json并且输出。
返回值处理是在请求流程的那个位置执行的呢?,是在处理器适配器(HandlerAdapter)调用处理方法执行完后执行的,处理完以后,能够解析出ModelAndView。供后续渲染需要,而由于返回值有不同的类型,那么就需要用一个通用的处理接口进行处理。这个接口就是HandlerMethodReturnValueHandler,它可以通用的是否支持MethodParameter类型,而后通用的处理MethodParameter的返回值类型,而MethodParameter就是方法、类、类型、以及子类扩展了返回值及类型,这样我们所有的处理方法和返回值组装到MethodParameter中,交给HandlerMethodReturnValueHandler这个通用的接口处理,这个就是处理器方法返回值处理的核心原理。
一、主要执行类
1、核心类描述
RequestMappingHandlerAdapter : 处理器适配器,请求主要执行类。
MethodReturnValueHandler : result处理类的顶级接口
+ boolean supportsReturnType(MethodParameter returnType); // 是否支持
+ void handleReturnValue(Object returnValue, MethodParameter returnType, // 进行处理
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;
MethodParameter: MethodParameter包装了执行方法以及各种方法参数
ReturnValueMethodParameter: MethodParameter子类,扩展了returnValue变量,并且执行paramIndex=-1,
ModelAndViewContainer : 包装了view和model,主要用来接收MethodReturnValueHandle的处理结果。
HandlerMethodReturnValueHandlerComposite : 组合各种result处理类。
常见MethodReturnValueHandler实现
1) ViewNameMethodReturnValueHandler : 用来处理String类型和void类型的返回值。
+ supportsReturnType // 判断是否支持String类型
+ handleReturnValue // 判断是否是rediect,并且将String类型的viewNmae设置到ModelAndViewContainer中
2) ModelAndViewMethodReturnValueHandler : 用来处理ModelAndView类型返回值
+ supportsReturnType // 判断是否支持ModelAndView 返回值
+ handleReturnValue // 将ModelAndView的数据复制给ModelAndViewContainer
3) RequestResponseBodyMethodProcessor : 用来处理responseBody注解的对象返回值
+ supportsReturnType // 判断类或方法是否有ResponseBody注解
+ handleReturnValue // 实际上调用的是多个HttpMessageConverter的处理方法。
2、类图结构
暂略
二、执行流程
1、主执行流程剖析
DispatcherServlet 的doService调用doDispatch,内部HandlerAdapter调用handle进行请求的处理我们常规默认请求的HandlerAdapter是RequestMappingHandlerAdapter进行处理。返回值处理主要是依靠HandlerMethodReturnValueHandlerComposite组装了多个MethodReturnValueHandler,这个composite组合 类的初始化时依靠实现initBean的afterPropertiesSet()中,这个和参数处理是一样的。
@Override
public void afterPropertiesSet() {
// ..... 省略
if (this.returnValueHandlers == null) {
List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
}
}
getDefaultReturnValueHandlers() {
// ..... 省略
handlers.add(new ModelAndViewMethodReturnValueHandler());
handlers.add(new ModelMethodProcessor());
handlers.add(new ViewMethodReturnValueHandler());
// ..... 省略
}
HandlerMethodReturnValueHandlerComposite的作用主要是组装多个返回值处理器,内部有2个方法 supportsReturnType是遍历所有的MethodParameter,如果有可以支持的就返回,如果有支持的处理器,就调用处理器进行处理。
接着RequestMappingHandlerAdapter的Handler方法,最终调用的是父类的invokeAndHandle方法,这个方法内部会执行handler并且调HandlerMethodReturnValueHandlerComposite的找支持的处理器调用处理器进行返回值处理。
public void invokeAndHandle(ServletWebRequest webRequest,
ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
// handler执行方法
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
if (returnValue == null) {
if (isRequestNotModified(webRequest) || hasResponseStatus() || mavContainer.isRequestHandled()) {
mavContainer.setRequestHandled(true); // 表示请求完成,没有后续操作
return;
}
}
mavContainer.setRequestHandled(false);
try { // 返回值处理方法
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
}
}
我们看看最常见的ViewNameMethodReturnValueHandler是怎么处理的。
// 通过判断返回值类型是否是String或者void,确定这个处理器是否支持,即String类型的都能支持。
public boolean supportsReturnType(MethodParameter returnType) {
Class<?> paramType = returnType.getParameterType();
return (void.class.equals(paramType) || String.class.equals(paramType));
}
// 处理String类型的返回值,主要是判断是否是rediect视图,如果是的设置标记,否则就直接将reutrnValue设置成viewName
public void handleReturnValue(Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
if (returnValue == null) { // 如果返回值为null,那么直接返回
return;
}
else if (returnValue instanceof String) {
String viewName = (String) returnValue;
mavContainer.setViewName(viewName);
if (isRedirectViewName(viewName)) {
mavContainer.setRedirectModelScenario(true);
}
}
}
我们在看看ModelAndViewMethodReturnValueHandler。
// 通过返回值是否是ModelAndView或它的子类型确定是否支持。
public boolean supportsReturnType(MethodParameter returnType) {
return ModelAndView.class.isAssignableFrom(returnType.getParameterType());
}
// 合并ModelAndView的属性到mavContainer
public void handleReturnValue(Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
if (returnValue == null) { // 如果返回值为null,设置请求完成,并且返回
mavContainer.setRequestHandled(true);
return;
}
ModelAndView mav = (ModelAndView) returnValue;
if (mav.isReference()) { // 设置viewName
String viewName = mav.getViewName();
mavContainer.setViewName(viewName);
// ....
} else {
View view = mav.getView();
mavContainer.setView(view);
// ....
}
mavContainer.addAllAttributes(mav.getModel()); // 合并model数据到mavContainer包装类中。
}
最后,我们再看看RequestResponseBodyMethodProcessor对@ResponseBody注解的返回值处理
// 通过判断类和方法是否有ResponseBody注解,确定类型是否支持。
public boolean supportsReturnType(MethodParameter returnType) {
return (AnnotationUtils.findAnnotation(returnType.getContainingClass(), ResponseBody.class) != null ||
returnType.getMethodAnnotation(ResponseBody.class) != null);
}
// 处理就是设置mavContainner已经处理完成,然后调用父类处理方法,原理就是根据返回值类型,和
// 请求的accept以及设置的返回值类型,最终实际上调用的是RequestMappingHandlerAdapter添加的
// 多个HttpMessageConverter处理。详情后面补充。
public void handleReturnValue(Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
throws IOException, HttpMediaTypeNotAcceptableException {
mavContainer.setRequestHandled(true);
writeWithMessageConverters(returnValue, returnType, webRequest);
}
经过RequestMappingHandlerAdapter的invokeAndHandle方法处理后,我们等于把返回值数据包装到了ModelAndViewContainer 这个对象中,从上面我们可以看到这个对象有一个变量mavContainer.setRequestHandled(true);即设置请求是否完成,比如ModelAndValue 返回类型,如果为null,那么就设置请求完成,@ResponseBody一律设置成请求完成,设置了这个变量值,后续处理会中断掉渲染视图的处理。
我们在RequestMappingHandlerAdapter的invokeAndHandle方法的最后,可以看到下面这段代码,即对我们返回值处理完的ModelAndViewContainer,又进行了getModelAndView的调用,这个方法的第二行做了判断,如果请求完成,就直接返回null,后续的判断就不会进行渲染视图等流程的操作。
return getModelAndView(mavContainer, modelFactory, webRequest);
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
modelFactory.updateModel(webRequest, mavContainer);
if (mavContainer.isRequestHandled()) { // 这里请求完成会返回null,后续判断不会进行渲染视图
return null;
}
// 设置model和view到ModelAndView中
ModelMap model = mavContainer.getModel();
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model);
if (!mavContainer.isViewReference()) {
mav.setView((View) mavContainer.getView());
}
// ....
return mav;
}
2、HttpMessageConverter的介绍
HttpMessageConverter是用来处理@ResponseBody注解的对象方法返回值,它是RequestMappingHandlerAdapter的List属性messageConverters通过构造函数初始化,初始注入了四个默认的处理。
public RequestMappingHandlerAdapter() {
StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
stringHttpMessageConverter.setWriteAcceptCharset(false); // see SPR-7316
this.messageConverters = new ArrayList<HttpMessageConverter<?>>(4);
this.messageConverters.add(new ByteArrayHttpMessageConverter());
this.messageConverters.add(stringHttpMessageConverter); // 处理string,因此string返回存在乱码问题
this.messageConverters.add(new SourceHttpMessageConverter<Source>());
this.messageConverters.add(new AllEncompassingFormHttpMessageConverter()); // 处理json
}
MappingJackson2HttpMessageConverter这个就是messageConverters的第四个元素。
具体过程比较复杂,涉及accept的mime类型,@RequestMapping设置的produces(返回值类型),等等繁杂处理,不进行介绍了。
贴一个MappingJackson2HttpMessageConverter处理ie返回json下载的自定义配置,结合这个自定义配置,可以在这个处理过程中,再深入了解其中原理。
<!--json转换器,配置解决ie返回json的下载bug-->
<bean id="mappingJackson2HttpMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>text/html;charset=UTF-8</value>
<value>text/json;charset=UTF-8</value>
<value>application/json;charset=UTF-8</value>
</list>
</property>
</bean>
<!--json转换器注入回RequestMappingHandlerAdapter.springmvc默认就支持responseBody注解 -->
<bean
class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="mappingJackson2HttpMessageConverter" /> <!-- JSON转换器 -->
</list>
</property>
</bean>
番外-组合模式:
spring-mvc处理方法的参数解析和返回值解析用到了一个组合模式的设计模式,即本文中介绍的HandlerMethodReturnValueHandlerComposite ,什么是组合模式,组合模式使得用户对单个对象和组合对象的使用具有一致性,即组合类实现单个类的接口,并且包装了多个单个类。下面是简易的demo,作为组合模式的一个案例模板。
/**
* 组合模式:组合模式使得用户对单个对象和组合对象的使用具有一致性
*/
public class MyCompositive {
public static void main(String[] args) {
HanlderCompositvie compositive = new HanlderCompositvie() ;
Hanlder hanlder1 = new IntegerHanler();
Hanlder hanlder2 = new StringHanlder();
compositive.addHandler(hanlder1);
compositive.addHandler(hanlder2);
MyClient client = new MyClient();
client.setHandlers(compositive);
Object result = client.handler(1);
System.out.println(result); // IntegerHanler1
Object result2 = client.handler("str");
System.out.println(result2); // StringHanlderstr
}
}
class MyClient {
HanlderCompositvie handlers = new HanlderCompositvie();
public HanlderCompositvie getHandlers() {
return handlers;
}
public void setHandlers(HanlderCompositvie handlers) {
this.handlers = handlers;
}
public Object handler(Object value) {
Class<? extends Object> type = value.getClass();
Object result = handlers.handler(value,type);
return result;
}
}
class HanlderCompositvie implements Hanlder {
private final List<Hanlder> hanlders = new ArrayList<Hanlder>();
@Override
public Object handler(Object vlaue,Class<?> type) {
Hanlder hanlder = getHandler(type);
Object returnValue = hanlder.handler(vlaue,type);
return returnValue;
}
@Override
public boolean support(Class<?> type) {
return getHandler(type) != null;
}
public HanlderCompositvie addHandler(Hanlder handler) {
hanlders.add(handler);
return this;
}
private Hanlder getHandler(Class<?> type) {
for (Hanlder hanlder : hanlders) {
if (hanlder.support(type)) {
return hanlder;
}
}
return null;
}
}
interface Hanlder {
boolean support(Class<?> type);
Object handler (Object value, Class<?> type);
}
class StringHanlder implements Hanlder {
@Override
public boolean support(Class<?> type) {
return String.class.equals(type);
}
@Override
public Object handler(Object value, Class<?> type) {
value = (String) value;
return "StringHanlder:"+ value;
}
}
class IntegerHanler implements Hanlder {
@Override
public boolean support(Class<?> type) {
return Integer.class.equals(type);
}
@Override
public Object handler(Object value, Class<?> type) {
value = (Integer) value;
return "IntegerHanler:" + value;
}
}