本文共 10978 字,大约阅读时间需要 36 分钟。
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) { Listhandlers = 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>(4); this.messageConverters.add(new ByteArrayHttpMessageConverter()); this.messageConverters.add(stringHttpMessageConverter); // 处理string,因此string返回存在乱码问题 this.messageConverters.add(new SourceHttpMessageConverter
MappingJackson2HttpMessageConverter这个就是messageConverters的第四个元素。
具体过程比较复杂,涉及accept的mime类型,@RequestMapping设置的produces(返回值类型),等等繁杂处理,不进行介绍了。
贴一个MappingJackson2HttpMessageConverter处理ie返回json下载的自定义配置,结合这个自定义配置,可以在这个处理过程中,再深入了解其中原理。
text/html;charset=UTF-8 text/json;charset=UTF-8 application/json;charset=UTF-8
番外-组合模式:
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 type = value.getClass(); Object result = handlers.handler(value,type); return result; }}class HanlderCompositvie implements Hanlder { private final Listhanlders = new ArrayList (); @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; }}
转载地址:http://kruni.baihongyu.com/