SpringMVC之分析HandlerMethodReturnValueHandler(一)

简介: 我们在用SpringMVC做web开发的时候,有时候处理完一个请求之后会返回一个页面,有时候会返回一个字符串,有时候会返回一个json对象。通过分析源码我们知道在调用请求处理器映射方法的时候走的是同一段代码,如下: org.

我们在用SpringMVC做web开发的时候,有时候处理完一个请求之后会返回一个页面,有时候会返回一个字符串,有时候会返回一个json对象。通过分析源码我们知道在调用请求处理器映射方法的时候走的是同一段代码,如下:

org.springframework.web.method.support.InvocableHandlerMethod#doInvoke

	protected Object doInvoke(Object... args) throws Exception {
		//反射设置setAccessible为true
		ReflectionUtils.makeAccessible(getBridgedMethod());
		try {
			//反射调用请求处理器映射方法
			return getBridgedMethod().invoke(getBean(), args);
		}
		............
	}
那么SpringMVC是在哪里进行不同返回对象的映射的呢?在org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod#invokeAndHandle中有这样一段代码:

		try {
			this.returnValueHandlers.handleReturnValue(
					returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
		}
这里的returnValueHandlers是HandlerMethodReturnValueHandlerComposite的实例(参考之前对HandlerMethodArgumentResolver的分析)。getReturnValueType(returnValue)获取到的是ReturnValueMethodParameter对象。这一段代码是用来处理不同的请求返回值的。所以我们不同请求的返回是由HandlerMethodReturnValueHandler的不同实现类来进行处理的。我们先看一下HandlerMethodReturnValueHandler这个接口的UML类图:




在HandlerMethodReturnValueHandler接口中有这样的两个方法:

//支持的返回值类型
boolean supportsReturnType(MethodParameter returnType);
//处理返回值
void handleReturnValue(Object returnValue, MethodParameter returnType,
		ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;

接下来我试着分析一下常用的几个不同返回值类型的场景。

首先我们先进入到org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#invokeHandlerMethod这个方法中,在这个方法中有这样的一句话:

invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);

通过之前的分析我们知道这个returnValueHandlers是在RequestMappingHandlerAdapter的afterPropertiesSet方法中实例化的HandlerMethodReturnValueHandlerComposite。(参考SpringMVC之分析RequestMappingHandlerAdapter(二))。接着会调用org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod#invokeAndHandle方法,从而调用org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite#handleReturnValue方法,我们进入到handleReturnValue这个方法中看一下:

	public void handleReturnValue(Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

		HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);//选择对应的HandlerMethodReturnValueHandler
		if (handler == null) {
			throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
		}//对返回值进行处理
		handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
	}
	private HandlerMethodReturnValueHandler selectHandler(Object value, MethodParameter returnType) {
		boolean isAsyncValue = isAsyncReturnValue(value, returnType);
		for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {//循环之前放入的HandlerMethodReturnValueHandler的实现类
			if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
				continue;
			}//调用supportsReturnType方法判断是否支持对返回值的处理
			if (handler.supportsReturnType(returnType)) {
				return handler;
			}
		}
		return null;
	}
在上面的这两个方法中通过调用HandlerMethodReturnValueHandler的supportsReturnType方法来判断支持返回值类型的处理类,通过调用HandlerMethodReturnValueHandler的handleReturnValue方法来对返回值进行处理。下面我们具体的分析几个场景。

ViewNameMethodReturnValueHandler

在分析这个类之前,我们先看一个例子:

@Controller
@RequestMapping("/returnValueHandler")
public class ReturnValueHandlerController {

    @RequestMapping("/viewNameMethod")
    public String viewNameMethodMapping() {
        return "viewNameMethod";
    }
}
我们访问一下这个请求:http://localhost:8086/returnValueHandler/viewNameMethod


这个错误很多人应该都见过吧。请求处理的结果是404。错误提示信息是:在/WEB-INF/jsp这个目录下找不到viewNameMethod.jsp这个文件。我们返回的是一个字符串,为什么这里是要去寻找viewNameMethod.jsp这个页面呢?秘密就在ViewNameMethodReturnValueHandler这个类中。在之前的分析中(SpringMVC之分析RequestMappingHandlerAdapter(二))我们知道,在RequestMappingHandlerAdapter中默认添加了一些列的HandlerMethodReturnValueHandler实现类,ViewNameMethodReturnValueHandler就在其中。我们看一下ViewNameMethodReturnValueHandler的supportsReturnType方法的内容:

	@Override
	public boolean supportsReturnType(MethodParameter returnType) {
		Class<?> paramType = returnType.getParameterType();
		return (void.class == paramType || CharSequence.class.isAssignableFrom(paramType));
	}
如果返回值是void或者是CharSequence类型(通常是值字符串),则可以使用ViewNameMethodReturnValueHandler这个类来处理。现在我们的返回值是String类型的,ViewNameMethodReturnValueHandler支持对String返回值的处理,所以我们这里获取到的HandlerMethodReturnValueHandler的实现类为ViewNameMethodReturnValueHandler。接下来我们看一下handleReturnValue这个方法的源码:

	@Override
	public void handleReturnValue(Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

		if (returnValue instanceof CharSequence) {//如果是字符类型
			String viewName = returnValue.toString();//获取字符串的值
			mavContainer.setViewName(viewName);//设置viewName的值
			if (isRedirectViewName(viewName)) {//判断是不是重定向
				mavContainer.setRedirectModelScenario(true);//设置为重定向
			}
		}
		else if (returnValue != null){//如果不是字符串类型,则抛出异常
			// should not happen
			throw new UnsupportedOperationException("Unexpected return type: " +
					returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
		}
	}
在上面的分析中我们可以看到,ViewNameMethodReturnValueHandler将我们返回的字符串映射为了视图名,所以这里就会走视图解析的流程,查找相应的视图,如果我们没有创建这个视图的话,就会报404的异常了。在上面的代码中还有一句isRedirectViewName。这个方法是判断请求是不是重定向到另外一个请求上。isRedirectViewName的源码如下:

	protected boolean isRedirectViewName(String viewName) {
		return (PatternMatchUtils.simpleMatch(this.redirectPatterns, viewName) || viewName.startsWith("redirect:"));
	}
如果返回的字符串是redirect:开头的,会认为这个请求会重定向到另外一个请求上面。

相关文章
|
11月前
|
容器
SpringMVC常见组件之HandlerExceptionResolver分析-2
SpringMVC常见组件之HandlerExceptionResolver分析-2
70 0
|
6月前
|
设计模式
SpringMVC常见组件之DataBinder数据绑定器分析
SpringMVC常见组件之DataBinder数据绑定器分析
318 0
|
11月前
|
XML 缓存 Java
SpringMVC常见组件之ViewResolver分析
本文我们尝试总结分析SpringMVC体系中的视图解析器-ViewResolver。其根据name解析视图View,通常鼓励实现类考虑国际化策略实现。
110 0
|
6月前
|
XML 存储 Java
SpringMVC常见组件之HandlerMapping分析
SpringMVC常见组件之HandlerMapping分析
154 0
|
6月前
|
XML 缓存 前端开发
SpringMVC常见组件之HandlerAdapter分析
SpringMVC常见组件之HandlerAdapter分析
79 0
|
11月前
|
XML 前端开发 Java
SpringMVC常见组件之View分析
SpringMVC常见组件之View分析
96 0
|
11月前
|
JSON 前端开发 Java
SpringMVC常见组件之HandlerExceptionResolver分析-1
SpringMVC常见组件之HandlerExceptionResolver分析-1
188 0
java202304java学习笔记第六十二天-ssm-springMvc中组件分析
java202304java学习笔记第六十二天-ssm-springMvc中组件分析
43 0
|
存储 设计模式 前端开发
浅谈SpringMVC五大组件以及对执行原理的分析。
Spring MVC是包含在spring中的一个基于MVC设计思想的Web应用程序框架,目的是简化开发工作,提高开发效率。
浅谈SpringMVC五大组件以及对执行原理的分析。
|
前端开发 Java 程序员
CORS跨域资源共享(三):@CrossOrigin/CorsFilter处理跨域请求示例及原理分析【享学Spring MVC】(下)
CORS跨域资源共享(三):@CrossOrigin/CorsFilter处理跨域请求示例及原理分析【享学Spring MVC】(下)
CORS跨域资源共享(三):@CrossOrigin/CorsFilter处理跨域请求示例及原理分析【享学Spring MVC】(下)