假装看源码之springmvc (四) springmvc的处理方法返回值处理

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

}

猜你喜欢

转载自blog.csdn.net/shuixiou1/article/details/113198944
今日推荐