博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
假装看源码之springmvc (四) springmvc的处理方法返回值处理
阅读量:4074 次
发布时间:2019-05-25

本文共 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) {					List
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
>(4); this.messageConverters.add(new ByteArrayHttpMessageConverter()); this.messageConverters.add(stringHttpMessageConverter); // 处理string,因此string返回存在乱码问题 this.messageConverters.add(new SourceHttpMessageConverter
()); this.messageConverters.add(new AllEncompassingFormHttpMessageConverter()); // 处理json}

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 List
hanlders = 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/

你可能感兴趣的文章
revenue
查看>>
currency
查看>>
iTunes Connect上传APP屏幕快照图片失败 - 您必须上传有效的屏幕快照。
查看>>
receipt
查看>>
[教程] Packt - Create a Game Environment with Blender and Unity by Darrin Lile
查看>>
[教程] Packt - Create a Game Environment with Blender and Unity by Darrin Lile
查看>>
详解Unity Profiler内存分析问题
查看>>
essential
查看>>
k8s安装
查看>>
c++ python 实现AES加密 基于openssl
查看>>
python正则提取mysql中文数据
查看>>
python刷赞
查看>>
python收集网页中的翻页
查看>>
成语接龙
查看>>
python3调用腾讯AI开放平台
查看>>
城市接龙
查看>>
Python操作Firefox
查看>>
python3调用OCR识别
查看>>
三个机器人尬聊
查看>>
世界杯买球算法
查看>>