【小家Spring】Spring MVC容器的web九大组件之---HandlerAdapter源码详解---一篇文章带你读懂返回值处理器HandlerMethodReturnValueHandler

每篇一句

粗心就是不会,没带作业就等于没做作业。请严格要求自己

相关阅读

【小家Spring】高性能关键技术之—体验Spring MVC的异步模式(Callable、WebAsyncTask、DeferredResult) 基础使用篇
【小家Spring】高性能关键技术之—体验Spring MVC的异步模式(ResponseBodyEmitter、SseEmitter、StreamingResponseBody) 高级使用篇
【小家Spring】Spring MVC容器的web九大组件之—HandlerMapping源码详解(一)—BeanNameUrlHandlerMapping系列
【小家Spring】Spring MVC容器的web九大组件之—HandlerMapping源码详解(二)—RequestMappingHandlerMapping系列

前言

Spring MVC处理入参靠的是HandlerMethodArgumentResolver这个接口,解析返回值靠的是HandlerMethodReturnValueHandler这个策略接口。

Spring MVC支持非常非常多的返回值类型,然后针对不同的返回值类型:比如Map、比如ViewName、比如Callable、比如异步的StreamingResponseBody等等都有其对应的处理器做处理,而它的顶层抽象为:HandlerMethodReturnValueHandler这个策略接口

知道了它的处理原理后,若我们有特殊的需要,我们自定义我们自己的返回值处理器,来自定义属于自己的返回值类型~~~~

还有比如我们需要对controller返回值前后做处理的情况,都可以在返回值上统一做手脚(比如加上接口执行耗时之类的~~~

HandlerMethodReturnValueHandler

这个接口的命名有点怪:处理函数返回值的处理器?其实它就是一个处理Controller返回值的接口

// @since 3.1  出现得相对还是比较晚的。因为`RequestMappingHandlerAdapter`也是这个时候才出来
// Strategy interface to handle the value returned from the invocation of a handler method
public interface HandlerMethodReturnValueHandler {

	// 每种处理器实现类,都对应着它能够处理的返回值类型~~~
	boolean supportsReturnType(MethodParameter returnType);

	// Handle the given return value by adding attributes to the model and setting a view or setting the
	// {@link ModelAndViewContainer#setRequestHandled} flag to {@code true} to indicate the response has been handled directly.
	// 简单的说就是处理返回值,可以处理着向Model里设置一个view
	// 或者ModelAndViewContainer#setRequestHandled设置true说我已经直接处理了,后续不要需要再继续渲染了~
	void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;

}

由此可以看出,它采用的又是一种责任链的设计模式。
因为SpringMVC支持的返回值类型众多,而我们绝大部分情况下是无需自己自定义返回值处理器的,因此下面我们可以看看它的继承树:
【小家Spring】Spring MVC容器的web九大组件之---HandlerAdapter源码详解---一篇文章带你读懂返回值处理器HandlerMethodReturnValueHandler_第1张图片
直接实现类众多,同时也有交给用户扩展的优先级子接口,后面会举例。
下面从上至下:

MapMethodProcessor

它相对来说比较特殊,既处理Map类型的入参,也处理Map类型的返回值(本文只关注返回值处理器部分

// @since 3.1
public class MapMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {

	@Override
	public boolean supportsParameter(MethodParameter parameter) {
		return Map.class.isAssignableFrom(parameter.getParameterType());
	}

	@Override
	@Nullable
	public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

		Assert.state(mavContainer != null, "ModelAndViewContainer is required for model exposure");
		return mavContainer.getModel();
	}

	// ==================上面是处理入参的,不是本文的重点~~~====================
	// 显然只有当你的返回值是个Map时候,此处理器才会生效
	@Override
	public boolean supportsReturnType(MethodParameter returnType) {
		return Map.class.isAssignableFrom(returnType.getParameterType());
	}

	@Override
	@SuppressWarnings({"unchecked", "rawtypes"})
	public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
		
		// 做的事非常简单,仅仅只是把我们的Map放进Model里面~~~
		// 但是此处需要注意的是:它并没有setViewName,所以它此时是没有视图名称的~~~
		// ModelAndViewContainer#setRequestHandled(true) 所以后续若还有处理器可以继续处理
		if (returnValue instanceof Map){
			mavContainer.addAllAttributes((Map) returnValue);
		}
	}

}

这里有必要特别注意一下JavaDoc部分的说明:

 * <p>A Map return value can be interpreted in more than one ways depending
 * on the presence of annotations like {@code @ModelAttribute} or
 * {@code @ResponseBody}. Therefore this handler should be configured after
 * the handlers that support these annotations.

直译为:它可以被解释为多种途径,依赖于Handler上面的额注解,如:@ModelAttribute@ResponseBody这种注解。所以请务必使它放在这些处理器的后面,最后执行~~~

若我们这么使用,什么注解都不标注,就会出问题了,如下:

    @GetMapping(value = "/hello")
    public Map helloGet() {
        Map<String, Object> map = new HashMap<>();
        map.put("name", "fsx");
        map.put("age", 18);
        return map;
    }

因为返回是Map类型,最终肯定会进入到MapMethodProcessor来处理返回值。但是因为没有setViewName,so访问的结果如下:
【小家Spring】Spring MVC容器的web九大组件之---HandlerAdapter源码详解---一篇文章带你读懂返回值处理器HandlerMethodReturnValueHandler_第2张图片
原因就是因为你没有viewName,SpringMVC采用了默认的获取viewName的机制(还是hello),所以进行转发的时候发现是相同的进入死循环,就抛错了~~~
【小家Spring】Spring MVC容器的web九大组件之---HandlerAdapter源码详解---一篇文章带你读懂返回值处理器HandlerMethodReturnValueHandler_第3张图片
走了一个默认获取viewName的机制。因此如果Handler上没有相关注解,直接使用是不妥的~

针对于此提供一种方案,来解决这种情况:既能定位到view,又能把Map放在Model里处理~~~



可否曾想过,Spring MVC提供给你一些处理器,你却不能直接使用???什么情况???
其实还是真的,Spring MVC提供给我们的只是个半成品,真正要想有好的效果,你还得自己加工,后面还会介绍好几个这样子的“半成品”~

下面就以MapMethodProcessor为例,在返回值上让它成为一个有用的东西:

// 对半成品`MapMethodProcessor`进行扩展,指向指定的视图即可~~~~
public class MyMapProcessor extends MapMethodProcessor {

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

        // 设置一个视图 方便渲染~
        mavContainer.setViewName("world");
    }
}

// 把自定义的返回值处理器,添加进Spring MVC里(实际上是`RequestMappingHandlerAdapter`里)
@Configuration
@EnableWebMvc
public class WebMvcConfig extends WebMvcConfigurerAdapter implements InitializingBean {

    @Autowired
    private RequestMappingHandlerAdapter requestMappingHandlerAdapter;

    @Override
    public void afterPropertiesSet() throws Exception {
        // 注意这里返回的是一个只读的视图  所以并不能直接操作里面的数据~~~~~
        List<HandlerMethodReturnValueHandler> returnValueHandlers = requestMappingHandlerAdapter.getReturnValueHandlers();

        List<HandlerMethodReturnValueHandler> result = new ArrayList<>();
        returnValueHandlers.forEach(r -> {
            // 换成我们自己的~~~~~~~
            if (r instanceof MapMethodProcessor) {
                result.add(new MyMapProcessor());
            } else {
                result.add(r);
            }
        });

        requestMappingHandlerAdapter.setReturnValueHandlers(result);

    }

    // ============这样只会在原来的15后面再添加一个,并不能起到联合的作用  所以只能使用上面的对原始类继承的方式~~~============
    //@Override
    //public void addReturnValueHandlers(List returnValueHandlers) {
    //    returnValueHandlers.add(new MyMapProcessor());
    //}
}

// 运行Controller就再也不会出现上面的报错了,而是能正常到world这个页面里面去~~~~
    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    public Map<String, Object> helloGet() {
        Map<String, Object> map = new HashMap<>();
        map.put("name", "fsx");
        map.put("age", 18);
        return map;
    }

其实还有另外一种方法就是我们自己配置一个视图解析器。比如指定前缀为WEB-INF/pages,后缀为.jsp,那么这个/hello请求最终自动会找到WEB-INF/pages/hello.jsp页面的`,相当于restful的url也能形成一种契约~~~

至于为何我们自定义的MyMapProcessor能够生效,因为我们已经实现了偷天换日~~~:
【小家Spring】Spring MVC容器的web九大组件之---HandlerAdapter源码详解---一篇文章带你读懂返回值处理器HandlerMethodReturnValueHandler_第4张图片
下面继续说到的一些“半成品”,各位若有兴趣,也可以自己配置一个视图解析器来处理(其实Spring MVC推荐是这么做的~,充分利用URL这种资源定位符



ViewNameMethodReturnValueHandler

从名字可以看出它处理的是ViewName,所以大概率处理的都是字符串类型的返回值~~~

处理返回值类型是void和String类型的。从Spring4.2之后,支持到了CharSequence类型。比如我们常见的String、StringBuffer、StringBuilder等都是没有问题的~

// 可以直接返回一个视图名,最终会交给`RequestToViewNameTranslator`翻译一下~~~
public class ViewNameMethodReturnValueHandler implements HandlerMethodReturnValueHandler {

	// Spring4.1之后支持自定义重定向的匹配规则
	// Spring4.1之前还只能支持redirect:这个固定的前缀~~~~
	private String[] redirectPatterns;
	public void setRedirectPatterns(String... redirectPatterns) {
		this.redirectPatterns = redirectPatterns;
	}
	public String[] getRedirectPatterns() {
		return this.redirectPatterns;
	}
	protected boolean isRedirectViewName(String viewName) {
		return (PatternMatchUtils.simpleMatch(this.redirectPatterns, viewName) || viewName.startsWith("redirect:"));
	}


	// 支持void和CharSequence类型(子类型)
	@Override
	public boolean supportsReturnType(MethodParameter returnType) {
		Class<?> paramType = returnType.getParameterType();
		return (void.class == paramType || CharSequence.class.isAssignableFrom(paramType));
	}

	// 注意:若返回值是void,此方法都不会进来
	@Override
	public void handleReturnValue(Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

		// 显然如果是void,returnValue 为null,不会走这里。
		// 也就是说viewName设置不了,所以会出现和上诉一样的循环报错~~~~~ 因此不要单独使用
		if (returnValue instanceof CharSequence) {
			String viewName = returnValue.toString();
			mavContainer.setViewName(viewName);
	
			// 做一个处理:如果是重定向的view,那就
			if (isRedirectViewName(viewName)) {
				mavContainer.setRedirectModelScenario(true);
			}
		}
		// 下面这都是不可达的~~~~~setRedirectModelScenario(true)标记一下
		// 此处不仅仅是else,而是还有个!=null的判断  
		// 那是因为如果是void的话这里返回值是null,属于正常的~~~~ 只是什么都不做而已~(viewName也没有设置哦~~~)
		else if (returnValue != null) {
			// should not happen
			throw new UnsupportedOperationException("Unexpected return type: " +
					returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
		}
	}

}

若handler直接这么使用:

    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    public void helloGet() {
        System.out.println("hello controller");
    }

浏览器获得的效果同上(Circular view path [hello]),所以不要单独使用。如果是返回字符串:它就自己回去找视图了:

    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    public String helloGet() {
        //return "hello"; // 若直接写hello,那不又到这个controller了吗,导致 Circular view path [hello]
        return "world";
    }

handler所有的方法,都不建议返回void,不管是页面还是json串~

ViewMethodReturnValueHandler

这个和上面非常类似,但是它的返回值不是一个字符串,而是一个View.class(它的实现类有很多,比如MappingJackson2JsonView、AbstractPdfView、MarshallingView、RedirectView、JstlView等等非常多的视图类型),进而渲染出一个视图给用户看。

// javadoc上有说明:此处理器需要配置在支持`@ModelAttribute`或者`@ResponseBody`的处理器们前面。防止它被取代~~~~
public class ViewMethodReturnValueHandler implements HandlerMethodReturnValueHandler {
	
	// 处理素有的View.class类型
	@Override
	public boolean supportsReturnType(MethodParameter returnType) {
		return View.class.isAssignableFrom(returnType.getParameterType());
	}

	@Override
	public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
			
		// 这个处理逻辑几乎完全同上
		// 最终也是为了mavContainer.setView(view);
		// 也会对重定向视图进行特殊的处理~~~~~~
		if (returnValue instanceof View) {
			View view = (View) returnValue;
			mavContainer.setView(view);
			if (view instanceof SmartView && ((SmartView) view).isRedirectView()) {
				mavContainer.setRedirectModelScenario(true);
			}
		}
		else if (returnValue != null) {
			// should not happen
			throw new UnsupportedOperationException("Unexpected return type: " +
					returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
		}
	}
}

Spring MVC认为后台产生的一份数据,可以是N多种方式来进行展示的。比如可议是Html页面、可议是word、可以是PDF、当然也可以是charts统计表格(比如我们古老的技术:JFrameChart就是生产报表的一把好手)~~

HttpHeadersReturnValueHandler

这个处理器非常有意思,它只处理请求头HttpHeaders。先效果~~~

    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    public HttpHeaders helloGet() {
        HttpHeaders httpHeaders = new HttpHeaders();

        // 这两个效果相同
        httpHeaders.add(HttpHeaders.CONTENT_TYPE, "application/json;charset=UTF-8");
        //httpHeaders.setContentType(MediaType.APPLICATION_JSON_UTF8);

        httpHeaders.add("name", "fsx");
        return httpHeaders;
    }

请求一下,浏览器没有任何内容。但是控制台里可以看到如下:
【小家Spring】Spring MVC容器的web九大组件之---HandlerAdapter源码详解---一篇文章带你读懂返回值处理器HandlerMethodReturnValueHandler_第5张图片
这个处理器可以帮助我们在需要对请求头进行特殊处理的时候,进行一定程度的加工。它Spring4.0后才有

public class HttpHeadersReturnValueHandler implements HandlerMethodReturnValueHandler {

	@Override
	public boolean supportsReturnType(MethodParameter returnType) {
		return HttpHeaders.class.isAssignableFrom(returnType.getParameterType());
	}

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

		// 请注意这里:已经标记该请求已经被处理过了~~~~~
		mavContainer.setRequestHandled(true);

		Assert.state(returnValue instanceof HttpHeaders, "HttpHeaders expected");
		HttpHeaders headers = (HttpHeaders) returnValue;

		// 返回值里自定义返回的响应头。这里会帮你设置到HttpServletResponse 里面去的~~~~
		if (!headers.isEmpty()) {
			HttpServletResponse servletResponse = webRequest.getNativeResponse(HttpServletResponse.class);
			Assert.state(servletResponse != null, "No HttpServletResponse");
			ServletServerHttpResponse outputMessage = new ServletServerHttpResponse(servletResponse);
			outputMessage.getHeaders().putAll(headers);
			outputMessage.getBody();  // flush headers
		}
	}

}

ModelMethodProcessor

MapMethodProcessor几乎一模一样。它处理org.springframework.ui.Model类型,处理方式几乎同Map方式一样(因为Model的结构和Map一样也是键值对)

ModelAndViewMethodReturnValueHandler

专门处理返回值类型是ModelAndView类型的。

ModelAndView = model + view + HttpStatus

public class ModelAndViewMethodReturnValueHandler implements HandlerMethodReturnValueHandler {

	// Spring4.1后一样  增加自定义重定向前缀的支持
	@Nullable
	private String[] redirectPatterns;
	public void setRedirectPatterns(@Nullable String... redirectPatterns) {
		this.redirectPatterns = redirectPatterns;
	}
	@Nullable
	public String[] getRedirectPatterns() {
		return this.redirectPatterns;
	}
	protected boolean isRedirectViewName(String viewName) {
		return (PatternMatchUtils.simpleMatch(this.redirectPatterns, viewName) || viewName.startsWith("redirect:"));
	}


	// 显然它只处理ModelAndView这种类型~
	@Override
	public boolean supportsReturnType(MethodParameter returnType) {
		return ModelAndView.class.isAssignableFrom(returnType.getParameterType());
	}

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

		// 如果调用者返回null 那就标注此请求被处理过了~~~~ 不需要再渲染了
		// 浏览器的效果就是:一片空白
		if (returnValue == null) {
			mavContainer.setRequestHandled(true);
			return;
		}

		ModelAndView mav = (ModelAndView) returnValue;
	
		// isReference()方法为:(this.view instanceof String)
		// 这里专门处理视图就是一个字符串的情况,else是处理视图是个View对象的情况
		if (mav.isReference()) {
			String viewName = mav.getViewName();
			mavContainer.setViewName(viewName);
			if (viewName != null && isRedirectViewName(viewName)) {
				mavContainer.setRedirectModelScenario(true);
			}
		}
		// 处理view  顺便处理重定向
		else {
			View view = mav.getView();
			mavContainer.setView(view);
			
			// 此处所有的view,只有RedirectView的isRedirectView()才是返回true,其它都是false
			if (view instanceof SmartView && ((SmartView) view).isRedirectView()) {
				mavContainer.setRedirectModelScenario(true);
			}
		}
		// 把status和model都设置进去
		mavContainer.setStatus(mav.getStatus());
		mavContainer.addAllAttributes(mav.getModel());
	}
}

ModelAndViewResolverMethodReturnValueHandler

这个就很厉害了,它是Spring MVC交给我们自定义返回值处理器的一个非常重要的渠道。从官方的javadoc里也能看出来:

 * This return value handler is intended to be ordered after all others as it
 * attempts to handle _any_ return value type (i.e. returns {@code true} for
 * all return types).

简单的说它是放在所有的其它的处理器最后一位的,所以它的supportsReturnType()是永远return true但它默认并没有给我们配置进来(而是我们根据需要自己选装~),装配的源码如下:

...
		// Catch-all
		if (!CollectionUtils.isEmpty(getModelAndViewResolvers())) {
			handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers()));
		}
		else {
			handlers.add(new ModelAttributeMethodProcessor(true));
		}
...

由此可见默认情况下它添加进来的是ModelAttributeMethodProcessor,但凡你RequestMappingHandlerAdapter#setModelAndViewResolvers()自己往里set了个ModelAndViewResolver,它就会被添加,进而让ModelAndViewResolver生效~。

ModelAndViewResolver它是一个接口,Spring并没有默认的实现类。Spring对它的定位很清楚:SPI for resolving custom return values from a specific handler method,它就是给我们自己来自定义处理返回值的一个处理器。通常用于检测特殊的返回类型,解析它们的已知结果值,下面我们自己玩一把试试~~~

public class MyModelAndViewResolver implements ModelAndViewResolver {

    @Override
    public ModelAndView resolveModelAndView(Method handlerMethod, Class<?> handlerType, Object returnValue, ExtendedModelMap implicitModel, NativeWebRequest webRequest) {
        System.out.println("...MyModelAndViewResolver...");
        if (returnValue instanceof Person) {
            ModelAndView modelAndView = new ModelAndView();

            Person person = (Person) returnValue;
            // 把属性值放进Model里
            implicitModel.addAttribute("name", person.name).addAttribute("age", person.age);
            modelAndView.setViewName("person");
            modelAndView.setStatus(HttpStatus.CREATED); //返回201的状态码
            return modelAndView;
        } else {
            return UNRESOLVED;
        }
    }
}

读源码发现我们重点就是要在RequestMappingHandlerAdapter这个Bean初始化,也就是执行afterPropertiesSet()方法的时候把ModelAndViewResolver给放进去,这样子就会生效了。

通读之后,我们发现WebMvcConfigurationSupport它的createRequestMappingHandlerAdapter()方法是受保护的。因此我们可以通过重新注册一个它来达到效果:

至于扩展Spring MVC采用WebMvcConfigurer接口还是继承WebMvcConfigurationSupport,建议参见:
WebMvcConfigurationSupport与WebMvcConfigurer的关系

因此我们只需要这么来定义即可:

@Configuration
@EnableWebMvc
public class WebMvcConfig extends WebMvcConfigurerAdapter {

	// 通过继承WebMvcConfigurationSupport 的方式去覆盖,前提是你对原理比较熟悉~
    @Configuration
    public static class MyWebMvcConfigurationSupport extends WebMvcConfigurationSupport {

        @Override
        protected RequestMappingHandlerAdapter createRequestMappingHandlerAdapter() {
            RequestMappingHandlerAdapter requestMappingHandlerAdapter = super.createRequestMappingHandlerAdapter();
            requestMappingHandlerAdapter.setModelAndViewResolvers(Arrays.asList(new MyModelAndViewResolver()));
            return requestMappingHandlerAdapter;
        }
    }
}

这样我们controller返回值类型如下:

    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    public Person helloGet() {
        Person person = new Person();
        person.name = "fsx";
        person.age = 18;
        return person;
    }

本来我们是不能够解析Person类型的,现在我们也能够正常解析了~~~~~ 这就是Spring MVC留给我们处理自定义类型的一个钩子,可以这么来用~~~

备注:好几个小伙伴问这个核心原理是什么,其实核心原理就是Bean定义的覆盖,希望可以举一反三,它是扩展Spring的一个较为常用的方式~

备注:ModelAndViewResolversetModelAndViewResolvers()的javadoc里可以看出,它一般用于来做向下兼容。如果你要自定义,一般需要重写HandlerMethodReturnValueHandlerModelAndViewResolver

ModelAndViewResolverMethodReturnValueHandler它的解释如下:

public class ModelAndViewResolverMethodReturnValueHandler implements HandlerMethodReturnValueHandler {

	@Nullable
	private final List<ModelAndViewResolver> mavResolvers;
	// 持有modelAttributeProcessor 的引用,所以是对它的一个加强~~~~
	private final ModelAttributeMethodProcessor modelAttributeProcessor = new ModelAttributeMethodProcessor(true);

	public ModelAndViewResolverMethodReturnValueHandler(@Nullable List<ModelAndViewResolver> mavResolvers) {
		this.mavResolvers = mavResolvers;
	}

	@Override
	public boolean supportsReturnType(MethodParameter returnType) {
		return true;
	}

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

		// 若我们配置了处理器,那就一个一个的处理吧~~~~~
		// 当然,最终真正处理的可能只有一个,这里也是责任链的形式~~~~一般会用if判断
		if (this.mavResolvers != null) {
			for (ModelAndViewResolver mavResolver : this.mavResolvers) {
				Class<?> handlerType = returnType.getContainingClass();
				Method method = returnType.getMethod();
				Assert.state(method != null, "No handler method");
				ExtendedModelMap model = (ExtendedModelMap) mavContainer.getModel();

		
				// 处理ModelAndView,若返回的不是ModelAndViewResolver.UNRESOLVED
				// 那就说明它处理了,那就return掉~~~~ 逻辑还是很简单的~~~
				ModelAndView mav = mavResolver.resolveModelAndView(method, handlerType, returnValue, model, webRequest);
		
				// 这一步相当于如果我们自定义了model,会把它的属性合并进来~~~
				// 大多数情况下,我们外部直接操作ExtendedModelMap model这个对象即可
				// 当然你也可以不指定view,自己写成同@ResponseBody一样的效果也是阔仪的
				if (mav != ModelAndViewResolver.UNRESOLVED) {
					mavContainer.addAllAttributes(mav.getModel());
					mavContainer.setViewName(mav.getViewName());
					if (!mav.isReference()) {
						mavContainer.setView(mav.getView());
					}
					return;
				}
			}
		}

		// No suitable ModelAndViewResolver...
		if (this.modelAttributeProcessor.supportsReturnType(returnType)) {
			this.modelAttributeProcessor.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
		}
		else {
			throw new UnsupportedOperationException("Unexpected return type: " +
					returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
		}
	}

}

ModelAttributeMethodProcessor

它是一个Processor,既处理入参封装,也处理返回值,本文只处理返回值部分。
@ModelAttribute能标注在入参处来处理入参,能标在方法处来处理方法返回值。源码部分也只展示处理返回值部分:

public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {

	// 默认值是false
	// true:
	private final boolean annotationNotRequired;
	public ModelAttributeMethodProcessor(boolean annotationNotRequired) {
		this.annotationNotRequired = annotationNotRequired;
	}

	// 方法上标注有@ModelAttribute这个注解
	// 或者annotationNotRequired为true并且是简单类型isSimpleProperty() = true
	// 简单类型释义:8大基本类型+包装类型+Enum+CharSequence+Number+Date+URI+Local+Class
	// 数组类型并且是简单的数组(上面那些类型的数组类型)类型  也算作简单类型
	@Override
	public boolean supportsReturnType(MethodParameter returnType) {
		return (returnType.hasMethodAnnotation(ModelAttribute.class) ||
				(this.annotationNotRequired && !BeanUtils.isSimpleProperty(returnType.getParameterType())));
	}

	// 做法相当简单,就是吧返回值放在model里面~~~~
	@Override
	public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

		if (returnValue != null) {
			
			// 这个方法:@ModelAttribute指明了name,那就以它的为准
			// 否则就是一个复杂的获取过程:string...
			String name = ModelFactory.getNameForReturnValue(returnValue, returnType);
			mavContainer.addAttribute(name, returnValue);
		}
	}
	
}

这个时候因为没有指定viewName,so~~~ 不解释

ServletModelAttributeMethodProcessor

它继承自ModelAttributeMethodProcessor。主要是对入参数据绑定方面做了一些方法的复写,支持到了Servlet等,它主要是对入参做了更多支持,因此本文先不做讨论。

Spring MVC内部实际应用中,ServletModelAttributeMethodProcessor仅仅被用于getDefaultArgumentResolvers()方法内。而ModelAttributeMethodProcessor都用于getDefaultReturnValueHandlers()内。当然它还用于ExceptionHandlerExceptionResolver~~~

AbstractMessageConverterMethodProcessor

一看它以Processor命名结尾,所以它既能处理入参,又能处理返回值。因此一样的,本文只关注返回值处理部分代码:

因为它都没有对supportsReturnTypehandleReturnValue进行实现,此抽象类暂时飘过~~~

其实它有一个非常重要的方法:writeWithMessageConverters(),下面详述

RequestResponseBodyMethodProcessor

它继承自AbstractMessageConverterMethodProcessor。从名字或许就能看出来,这个处理器及其重要,因为它处理着我们最为重要的一个注解@ResponseBody(其实它还处理@RequestBody,只是我们这部分不讲请求参数~~~) 并且它在读、写的时候和HttpMessageConverter还有深度结合~~

public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {

	// 显然可以发现,方法上或者类上标注有@ResponseBody都是可以的~~~~
	// 这也就是为什么现在@RestController可以代替我们的的@Controller + @ResponseBody生效了
	@Override
	public boolean supportsReturnType(MethodParameter returnType) {
		return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
				returnType.hasMethodAnnotation(ResponseBody.class));
	}

	@Override
	public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
			throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
		
		// 首先就标记:此请求已经被处理了~~~
		mavContainer.setRequestHandled(true);
		ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
		ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);

		// Try even with null return value. ResponseBodyAdvice could get involved.
		// 这个方法是核心,也会处理null值~~~  这里面一些Advice会生效~~~~
		// 会选择到合适的HttpMessageConverter,然后进行消息转换~~~~(这里只指写~~~)  这个方法在父类上,是非常核心关键自然也是非常复杂的~~~
		writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
	}
}
HttpEntityMethodProcessor

显然它是处理返回值为HttpEntity类型的。

public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodProcessor {

	// 看这个判断。绝大多数情况下我们使用的返回值是ResponseEntity
	// 当然你也可以直接使用HttpEntity 作为返回值也是可以的
	@Override
	public boolean supportsReturnType(MethodParameter returnType) {
		return (HttpEntity.class.isAssignableFrom(returnType.getParameterType()) &&
				!RequestEntity.class.isAssignableFrom(returnType.getParameterType()));
	}
	
	// 它的handleReturnValue方法就不详细说了,在RequestResponseBodyMethodProcessor的基础上主要做了如下增强:
	// 1、对请求中有Vary的进行特殊处理
	// 2、Http状态码是200的话。如果是get请求或者Head请求,并且内容没有改变isResourceNotModified  那就直接outputMessage.flush()  然后return掉
	// 3、做Http状态码是3打头(returnStatus / 100 == 3),如果有location的key,就特殊处理
	// 最终,最终。正常情况下:依然同上调用父类writeWithMessageConverters()方法~~~
}

显然,若我们想自己设置管理Http状态码,可以使用ResponseEntity。但显然绝大多数情况下,我们使用@ResponseBody更加的便捷~~~~



因为这块特别重要,所以这里逃不开的要深入了解。毕竟它还和非常重要消息转换器也有非常重要的联系。所以要对父类方法writeWithMessageConverters()进行深入的解释:

你会发现其它的返回值处理器都是不会调用消息转换器的,而只有AbstractMessageConverterMethodProcessor它的两个子类才会这么做。而刚巧,这种方式(@ResponseBody方式)是我们当下最为流行的处理方式,因此非常有必要进行深入的了解~~~

AbstractMessageConverterMethodProcessor#writeWithMessageConverters详解

为了方便讲解,此处我们采用解析此处理器结合讲解:

    @ResponseBody
    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    public Person helloGet() {
        Person person = new Person();
        person.name = "fsx";
        person.age = 18;
        return person;
    }

很显然它标注了@ResponseBody,所以最终会调用ResponseBodyEmitterReturnValueHandler进行转换、解析~~~~

// @since 3.1  会发现它也处理请求,但是不是本文讨论的重点
//return values by writing to the response with {@link HttpMessageConverter HttpMessageConverters}
public abstract class AbstractMessageConverterMethodProcessor extends AbstractMessageConverterMethodArgumentResolver
		implements HandlerMethodReturnValueHandler {
	...
	// 此处我们只关注它处理返回值的和信访方法
	// Writes the given return type to the given output message
	// 从JavaDoc解释可以看出,它的作用很“单一“:就是把返回值写进output message~~~
	@SuppressWarnings({"rawtypes", "unchecked"})
	protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,
			ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
			throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {

		Object body;
		Class<?> valueType;
		Type targetType;

		// 注意此处的特殊处理,相当于把所有的CharSequence类型的,都最终当作String类型处理的~
		if (value instanceof CharSequence) {
			body = value.toString();
			valueType = String.class;
			targetType = String.class;
		}
		// 我们本例;body为返回值对象  Person@5229
		// valueType为:class com.fsx.bean.Person
		// targetType:class com.fsx.bean.Person
		else {
			body = value;
			valueType = getReturnValueType(body, returnType);
	
			// 此处相当于兼容了泛型类型的处理
			targetType = GenericTypeResolver.resolveType(getGenericType(returnType), returnType.getContainingClass());
		}

		// 若返回值是个org.springframework.core.io.Resource  就走这里  此处忽略~~
		if (isResourceType(value, returnType)) {
			outputMessage.getHeaders().set(HttpHeaders.ACCEPT_RANGES, "bytes");
			if (value != null && inputMessage.getHeaders().getFirst(HttpHeaders.RANGE) != null &&
					outputMessage.getServletResponse().getStatus() == 200) {
				Resource resource = (Resource) value;
				try {
					List<HttpRange> httpRanges = inputMessage.getHeaders().getRange();
					outputMessage.getServletResponse().setStatus(HttpStatus.PARTIAL_CONTENT.value());
					body = HttpRange.toResourceRegions(httpRanges, resource);
					valueType = body.getClass();
					targetType = RESOURCE_REGION_LIST_TYPE;
				}
				catch (IllegalArgumentException ex) {
					outputMessage.getHeaders().set(HttpHeaders.CONTENT_RANGE, "bytes */" + resource.contentLength());
					outputMessage.getServletResponse().setStatus(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE.value());
				}
			}
		}

		// selectedMediaType表示最终被选中的MediaType,毕竟请求放可能是接受N多种MediaType的~~~
		MediaType selectedMediaType = null;
		// 一般情况下 请求方很少会指定contentType的~~~
		// 如果请求方法指定了,就以它的为准,就相当于selectedMediaType 里面就被找打了
		// 否则就靠系统自己去寻找到一个最为合适的~~~
		MediaType contentType = outputMessage.getHeaders().getContentType();
		if (contentType != null && contentType.isConcrete()) {
			if (logger.isDebugEnabled()) {
				logger.debug("Found 'Content-Type:" + contentType + "' in response");
			}
			selectedMediaType = contentType;
		}
		else {
			HttpServletRequest request = inputMessage.getServletRequest();
			// 前面我们说了 若是谷歌浏览器  默认它的accept为:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/s
			// 所以此处数组解析出来有7对
			List<MediaType> acceptableTypes = getAcceptableMediaTypes(request);

			// 这个方法就是从所有已经注册的转换器里面去找,看看哪些转换器.canWrite,然后把他们所支持的MediaType都加入进来~~~
			// 比如此例只能匹配到MappingJackson2HttpMessageConverter,所以匹配上的有application/json、application/*+json两个
			List<MediaType> producibleTypes = getProducibleMediaTypes(request, valueType, targetType);
			
			// 这个异常应该我们经常碰到:有body体,但是并没有能够支持的转换器,就是这额原因~~~
			if (body != null && producibleTypes.isEmpty()) {
				throw new HttpMessageNotWritableException("No converter found for return value of type: " + valueType);
			}

			// 下面相当于从浏览器可议接受的MediaType里面,最终抉择出N个来
			// 原理也非常简单:你能接受的isCompatibleWith上了我能处理的,那咱们就好说,处理就完了
			List<MediaType> mediaTypesToUse = new ArrayList<>();
			for (MediaType requestedType : acceptableTypes) {
				for (MediaType producibleType : producibleTypes) {
					if (requestedType.isCompatibleWith(producibleType)) {
					// 从两个中选择一个最匹配的  主要是根据q值来比较  排序
					// 比如此例,最终匹配上的有两个:application/json;q=0.8和application/*+json;q=0.8
						mediaTypesToUse.add(getMostSpecificMediaType(requestedType, producibleType));
					}
				}
			}
			
			// 这个异常也不少见,比如此处如果没有导入Jackson相关依赖包
			// 就会抛出这个异常了:HttpMediaTypeNotAcceptableException:Could not find acceptable representation
			if (mediaTypesToUse.isEmpty()) {
				if (body != null) {
					throw new HttpMediaTypeNotAcceptableException(producibleTypes);
				}
				if (logger.isDebugEnabled()) {
					logger.debug("No match for " + acceptableTypes + ", supported: " + producibleTypes);
				}
				return;
			}
			
			// 根据Q值进行排序:
			MediaType.sortBySpecificityAndQuality(mediaTypesToUse);
			// 因为已经排过
			for (MediaType mediaType : mediaTypesToUse) {
				if (mediaType.isConcrete()) {
					selectedMediaType = mediaType;
					break;
				}
				else if (mediaType.isPresentIn(ALL_APPLICATION_MEDIA_TYPES)) {
					selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;
					break;
				}
			}

			if (logger.isDebugEnabled()) {
				logger.debug("Using '" + selectedMediaType + "', given " +
						acceptableTypes + " and supported " + producibleTypes);
			}
		}

		// 最终的最终 都会找到一个决定write的类型,必粗此处为:application/json;q=0.8
		//  因为最终会决策出来一个MediaType,所以此处就是要根据此MediaType找到一个合适的消息转换器,把body向outputstream写进去~~~
		// 注意此处:是RequestResponseBodyAdviceChain执行之处~~~~
		if (selectedMediaType != null) {
			selectedMediaType = selectedMediaType.removeQualityValue();
			for (HttpMessageConverter<?> converter : this.messageConverters) {

		
				// 从这个判断可以看出 ,处理body里面内容,GenericHttpMessageConverter类型的转换器是优先级更高,优先去处理的
				GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);
				if (genericConverter != null ? ((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :
						converter.canWrite(valueType, selectedMediaType)) {

					// 在写body之前执行~~~~  会调用我们注册的所有的合适的ResponseBodyAdvice#beforeBodyWrite方法
					// 相当于在写之前,我们可以介入对body体进行处理
					body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,
							(Class<? extends HttpMessageConverter<?>>) converter.getClass(),
							inputMessage, outputMessage);
					if (body != null) {
						Object theBody = body;
						LogFormatUtils.traceDebug(logger, traceOn ->
								"Writing [" + LogFormatUtils.formatValue(theBody, !traceOn) + "]");

						// 给响应Response设置一个Content-Disposition的请求头(若需要的话)  若之前已经设置过了,此处将什么都不做
						// 比如我们常见的:response.setHeader("Content-Disposition", "attachment; filename=" + java.net.URLEncoder.encode(fileName, "UTF-8"));
						//Content-disposition 是 MIME 协议的扩展,MIME 协议指示 MIME 用户代理如何显示附加的文件。
						// 当 Internet Explorer 接收到头时,它会激活文件下载对话框,它的文件名框自动填充了头中指定的文件名
						addContentDispositionHeader(inputMessage, outputMessage);
						if (genericConverter != null) {
							genericConverter.write(body, targetType, selectedMediaType, outputMessage);
						}
						else {
							((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
						}
					}
					// 如果return null,body里面是null 那就啥都不写,输出一个debug日志即可~~~~
					else {
						if (logger.isDebugEnabled()) {
							logger.debug("Nothing to write: null body");
						}
					}

					// 这一句表示:只要一个一个消息转换器处理了,就立马停止~~~~
					return;
				}
			}
		}

		if (body != null) {
			throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes);
		}
	}
	...
}

从上分析可以看出,这里面也提供了ResponseBodyAdvice钩子,我们可以通过实现此接口,来对接口的返回值进行干预、修改。相关注解为:@ControllerAdvice、@RestControllerAdvice
比如我下面这个可以让所有的@ResponseBody的处理器都返回固定值"hello,world"

@ControllerAdvice
public class MyResponseBodyAdvice implements ResponseBodyAdvice<Object> {

    @Override
    public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
        return "hello,world";
    }
}

这样,访问任何这种rest请求,返回的都是:
在这里插入图片描述

这里面还是有个问题的:我们发现返回的hello world没错,,但是却都是带双引号的,显然这不是我想要的呀。怎么回事?怎么办呢?

原因,其实了解了上面的原理就能知道了。因为执行我们的MyResponseBodyAdvice#beforeBodyWrite此时候消息转换器已经选好了:MappingJackson2HttpMessageConverter

它最后调用writer方法其实底层其实就是调用objectMapper.writeValueAsString()进行写入,而为何会有双引号,看下面这个ObjectMapper的例子就一目了然了:

    public static void main(String[] args) throws JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper();
        System.out.println(objectMapper.writeValueAsString("hello world")); // "hello world"  两边有分号

        Person person = new Person();
        person.name = "fsx";
        person.age = 18;
        System.out.println(objectMapper.writeValueAsString(person)); // {"name":"fsx","age":18}   非常正规的json数据
    }

解决办法:参考StringHttpMessageConverter写字符串的方法,然后自己进一步替换默认操作~~(自定义消息转换器)



AsyncHandlerMethodReturnValueHandler

它是一个子接口,增加了一个方法。这个接口是Spring4.2提供的,挺有意思的一个接口,Spring内部并没有提供任何实现。

// @since 4.2
// 支持异步类型的返回值处理程序。此类返回值类型需要优先处理,以便异步值可以“展开”。
// 异步实现此接口并不是必须的,但是若你需要在处理程序之前执行,就需要实现这个接口了~~~
// 因为默认情况下:我们自定义的Handler它都是在内置的Handler后面去执行的~~~~
public interface AsyncHandlerMethodReturnValueHandler extends HandlerMethodReturnValueHandler {
	// 给定的返回值是否表示异步计算
	boolean isAsyncReturnValue(@Nullable Object returnValue, MethodParameter returnType);
}

需要注意的是,这个接口和异步好像并没有任何关系,只是体现出了它的优先级。

因为默认情况下我们定义custom自己的处理器,排名都是靠后的。但是如果你定义了一个实现类,实现的是AsyncHandlerMethodReturnValueHandler这个子接口,你的排名就会靠前执行了~~~

由于上面已有类似的例子了,此处就不花篇幅举例了。



关于Spring MVC异步处理的几个返回值处理器

因为Spring MVC支持多种异步返回的方式,因此放在此处一起讲。推荐先参考博文:
【小家Spring】高性能关键技术之—体验Spring MVC的异步模式(Callable、WebAsyncTask、DeferredResult) 基础使用篇

StreamingResponseBodyReturnValueHandler

Spring4.2才出来。(因为StreamingResponseBody是Spring4.2才出来的~~~它很方便做文件下载)

public class StreamingResponseBodyReturnValueHandler implements HandlerMethodReturnValueHandler {

	// 显然这里支持返回值直接是StreamingResponseBody类型,也支持你用`ResponseEntity`在包一层
	// ResponseEntity的泛型类型必须是StreamingResponseBody类型~~~
	@Override
	public boolean supportsReturnType(MethodParameter returnType) {
		if (StreamingResponseBody.class.isAssignableFrom(returnType.getParameterType())) {
			return true;
		} else if (ResponseEntity.class.isAssignableFrom(returnType.getParameterType())) {
			Class<?> bodyType = ResolvableType.forMethodParameter(returnType).getGeneric().resolve();
			return (bodyType != null && StreamingResponseBody.class.isAssignableFrom(bodyType));
		}
		return false;
	}

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

		// 从这句代码也可以看出,只有返回值为null了,它才关闭,否则可以持续不断的向response里面写东西
		if (returnValue == null) {
			mavContainer.setRequestHandled(true);
			return;
		}

		HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
		Assert.state(response != null, "No HttpServletResponse");
		ServerHttpResponse outputMessage = new ServletServerHttpResponse(response);

		// 从ResponseEntity里body提取出来~~~~~
		if (returnValue instanceof ResponseEntity) {
			ResponseEntity<?> responseEntity = (ResponseEntity<?>) returnValue;
			response.setStatus(responseEntity.getStatusCodeValue());
			outputMessage.getHeaders().putAll(responseEntity.getHeaders());
			returnValue = responseEntity.getBody();
			if (returnValue == null) {
				mavContainer.setRequestHandled(true);
				outputMessage.flush();
				return;
			}
		}

		ServletRequest request = webRequest.getNativeRequest(ServletRequest.class);
		Assert.state(request != null, "No ServletRequest");
		ShallowEtagHeaderFilter.disableContentCaching(request); // 禁用内容缓存

		Assert.isInstanceOf(StreamingResponseBody.class, returnValue, "StreamingResponseBody expected");
		StreamingResponseBody streamingBody = (StreamingResponseBody) returnValue;

		// 最终也是开启了一个Callable 任务,最后交给WebAsyncUtils去执行的~~~~
		Callable<Void> callable = new StreamingResponseBodyTask(outputMessage.getBody(), streamingBody);

		// WebAsyncUtils.getAsyncManager得到的是一个`WebAsyncManager`对象
		// startCallableProcessing会把callable任务都包装成一个`WebAsyncTask`,最终交给`AsyncTaskExecutor`执行
		// 至于异步的详细执行原理,请参考上面的相关博文,此处只点一下~~~~
		WebAsyncUtils.getAsyncManager(webRequest).startCallableProcessing(callable, mavContainer);
	}

	// 这个任务很简单,实现了Callable的call方法,它就是相当于启一个线程,把本次body里面的内容写进response输出流里面~~~
	// 但是此时输出流并不会关闭~~~~
	private static class StreamingResponseBodyTask implements Callable<Void> {

		private final OutputStream outputStream;

		private final StreamingResponseBody streamingBody;

		public StreamingResponseBodyTask(OutputStream outputStream, StreamingResponseBody streamingBody) {
			this.outputStream = outputStream;
			this.streamingBody = streamingBody;
		}

		@Override
		public Void call() throws Exception {
			this.streamingBody.writeTo(this.outputStream);
			return null;
		}
	}

}

可以看出它的原理是自己构建出一个内部的异步线程,交给reponse的异步上下文去处理。

由上面代码课件,它不仅仅支持json内容,也是支持一直返回页面渲染的内容的。(只是大多数情况下我们把它和@ResponseBody联合使用)

DeferredResultMethodReturnValueHandler

这个也许是我们最为常用的一种异步处理方式。它不仅仅处理返回值类型为DeferredResult,也会处理返回值类型为ListenableFutureCompletionStage(Java8新增的接口)类型的

public class DeferredResultMethodReturnValueHandler implements HandlerMethodReturnValueHandler {

	// 它支持处理丰富的数据类型
	@Override
	public boolean supportsReturnType(MethodParameter returnType) {
		Class<?> type = returnType.getParameterType();
		return (DeferredResult.class.isAssignableFrom(type) ||
				ListenableFuture.class.isAssignableFrom(type) ||
				CompletionStage.class.isAssignableFrom(type));
	}

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

		// 一样的  只有返回null了才代表此请求处理完成了
		if (returnValue == null) {
			mavContainer.setRequestHandled(true);
			return;
		}

		DeferredResult<?> result;

		// 此处是适配器模式的使用,最终都适配成了一个DeferredResult(使用的内部类实现的~~~)
		if (returnValue instanceof DeferredResult) {
			result = (DeferredResult<?>) returnValue;
		} else if (returnValue instanceof ListenableFuture) {
			result = adaptListenableFuture((ListenableFuture<?>) returnValue);
		} else if (returnValue instanceof CompletionStage) {
			result = adaptCompletionStage((CompletionStage<?>) returnValue);
		} else {
			// Should not happen...
			throw new IllegalStateException("Unexpected return value type: " + returnValue);
		}
		// 此处调用的异步方法是:startDeferredResultProcessing
		WebAsyncUtils.getAsyncManager(webRequest).startDeferredResultProcessing(result, mavContainer);
	}

	// 下为两匿名内部实现类做的兼容适配、兼容处理~~~~~非常的简单~~~~
	private DeferredResult<Object> adaptListenableFuture(ListenableFuture<?> future) {
		DeferredResult<Object> result = new DeferredResult<>();
		future.addCallback(new ListenableFutureCallback<Object>() {
			@Override
			public void onSuccess(@Nullable Object value) {
				result.setResult(value);
			}
			@Override
			public void onFailure(Throwable ex) {
				result.setErrorResult(ex);
			}
		});
		return result;
	}

	private DeferredResult<Object> adaptCompletionStage(CompletionStage<?> future) {
		DeferredResult<Object> result = new DeferredResult<>();
		future.handle((BiFunction<Object, Throwable, Object>) (value, ex) -> {
			if (ex != null) {
				result.setErrorResult(ex);
			}
			else {
				result.setResult(value);
			}
			return null;
		});
		return result;
	}

}

CallableMethodReturnValueHandler

因为已经解释了StreamingResponseBodyReturnValueHandler,它最终也是转换为一个Callable去处理了的。因此此处返回值直接是callable,简直就不要太简单了~~~

ResponseBodyEmitterReturnValueHandler

XXXEmitter它相当于加强版的DeferredResult,它可以返回多个值给客户端。其实它的底层原理还是DeferredResult,此处不再做过多的介绍~~~~

AsyncTaskMethodReturnValueHandler

顾名思义,它是专门处理返回值类型为WebAsyncTask的异步请求形式。

// @since 3.2  因为WebAsyncTask这个时候才出来~~~
public class AsyncTaskMethodReturnValueHandler implements HandlerMethodReturnValueHandler {

	@Nullable
	private final BeanFactory beanFactory;

	public AsyncTaskMethodReturnValueHandler(@Nullable BeanFactory beanFactory) {
		this.beanFactory = beanFactory;
	}

	@Override
	public boolean supportsReturnType(MethodParameter returnType) {
		return WebAsyncTask.class.isAssignableFrom(returnType.getParameterType());
	}

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

		if (returnValue == null) {
			mavContainer.setRequestHandled(true);
			return;
		}

		WebAsyncTask<?> webAsyncTask = (WebAsyncTask<?>) returnValue;
		if (this.beanFactory != null) {
			webAsyncTask.setBeanFactory(this.beanFactory);
		}
		// 我们发现它使用的也是startCallableProcessing...
	WebAsyncUtils.getAsyncManager(webRequest).startCallableProcessing(webAsyncTask, mavContainer);
	}
}

代码逻辑非常简单



HandlerMethodReturnValueHandlerComposite:处理器合成

这是个厉害角色。其实它就是提供的所有的HandlerMethodReturnValueHandler集合,它定义了一个链表用于存储所有实现的HandlerMethodReturnValueHandler

它用在RequestMappingHandlerAdapterExceptionHandlerExceptionResolver里,此处以RequestMappingHandlerAdapter为例:

public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
		implements BeanFactoryAware, InitializingBean {
	// 这里保存在用户自定义的一些处理器,大部分情况下无需自定义~~~
	@Nullable
	private List<HandlerMethodReturnValueHandler> customReturnValueHandlers;
	// 保存着所有的处理器~~~~上面custom自定义的最终也会放进来,放在尾部
	// 从它的命名似乎可议看出,它就是汇总~~~
	@Nullable
	private HandlerMethodReturnValueHandlerComposite returnValueHandlers;

	// 可以看到即使你调用了set方法,最终也是会给你生成一个HandlerMethodReturnValueHandlerComposite
	public void setReturnValueHandlers(@Nullable List<HandlerMethodReturnValueHandler> returnValueHandlers) {
		if (returnValueHandlers == null) {
			this.returnValueHandlers = null;
		} else {
			this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite();
			this.returnValueHandlers.addHandlers(returnValueHandlers);
		}
	}
	@Nullable
	public List<HandlerMethodReturnValueHandler> getReturnValueHandlers() {
		return (this.returnValueHandlers != null ? this.returnValueHandlers.getHandlers() : null);
	}

	// 它的初始化发生在这:
	@Override
	public void afterPropertiesSet() {
		...
		// 相当于你自己没有set,那就交给Spring自己去处理吧~~~~
		if (this.returnValueHandlers == null) {
			// 这个getDefaultReturnValueHandlers()会装载15个左右的返回值处理器,可以说覆盖我们日常开发的所有
			// 若你自己自定义了custom的,放进了customReturnValueHandlers里,最终也会被加进来放进去~~~~ 放在末尾~~~~
			List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
			this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
		}
	}
}

Composite:混合成的,由此可见它就是和汇总的作用。
那么接下来,就看看它本尊自身,提供了哪些能力?其实它的代码量不大:

// 首先发现,它也实现了接口HandlerMethodReturnValueHandler 
// 它会缓存以前解析的返回类型以加快查找速度
public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodReturnValueHandler {

	private final List<HandlerMethodReturnValueHandler> returnValueHandlers = new ArrayList<>();
	// 返回的是一个只读视图
	public List<HandlerMethodReturnValueHandler> getHandlers() {
		return Collections.unmodifiableList(this.returnValueHandlers);
	}
	public HandlerMethodReturnValueHandlerComposite addHandler(HandlerMethodReturnValueHandler handler) {
		this.returnValueHandlers.add(handler);
		return this;
	}
	public HandlerMethodReturnValueHandlerComposite addHandlers( @Nullable List<? extends HandlerMethodReturnValueHandler> handlers) {
		if (handlers != null) {
			this.returnValueHandlers.addAll(handlers);
		}
		return this;
	}
	
	// 由这两个可议看出,但凡有一个Handler支持处理这个返回值,就是支持的~~~
	@Override
	public boolean supportsReturnType(MethodParameter returnType) {
		return getReturnValueHandler(returnType) != null;
	}
	@Nullable
	private HandlerMethodReturnValueHandler getReturnValueHandler(MethodParameter returnType) {
		for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
			if (handler.supportsReturnType(returnType)) {
				return handler;
			}
		}
		return null;
	}

	// 这里就是处理返回值的核心内容~~~~~
	@Override
	public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

		// selectHandler选择收个匹配的Handler来处理这个返回值~~~~ 若一个都木有找到  抛出异常吧~~~~
		// 所有很重要的一个方法是它:selectHandler()  它来匹配,以及确定优先级
		HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
		if (handler == null) {
			throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
		}
		handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
	}

	// 根据返回值,以及返回类型  来找到一个最为合适的HandlerMethodReturnValueHandler
	@Nullable
	private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
		// 这个和我们上面的就对应上了  第一步去判断这个返回值是不是一个异步的value(AsyncHandlerMethodReturnValueHandler实现类只能我们自己来写~)
		boolean isAsyncValue = isAsyncReturnValue(value, returnType);
		for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
			// 如果判断发现这个值是异步的value,那它显然就只能交给你自己定义的异步处理器处理了,别的处理器肯定就靠边站~~~~~
			if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
				continue;
			}
			if (handler.supportsReturnType(returnType)) {
				return handler;
			}
		}
		return null;
	}
	private boolean isAsyncReturnValue(@Nullable Object value, MethodParameter returnType) {
		for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
			if (handler instanceof AsyncHandlerMethodReturnValueHandler && ((AsyncHandlerMethodReturnValueHandler) handler).isAsyncReturnValue(value, returnType)) {
				return true;
			}
		}
		return false;
	}
}

我们可以看到,它内的逻辑其实非常的简单。重点在于我们需要关心下调用栈:

请求的入口处在这:doDispatcher里会找到一个HandlerAdapter会调用@handle方法来真正执行Spring MVC的Handler。扔给ServletInvocableHandlerMethod#invokeAndHandle去执行处理器,从而拿到方法返回值:returnValue
最终交给HandlerMethodReturnValueHandlerComposite#handleReturnValue它去处理~~~上面看了源码处理过程,这就简单了,其实最终做事的是我们的具体的找到唯一的一个HandlerMethodReturnValueHandler~

Spring MVC默认配置返回值处理器们

不管开启@EnableWebMvc还是未开启,都是15个

备注:如果是Spring5一下的版本,若未开启@EnableWebMvc,处理的类是过时的AnnotationMethodHandlerAdapter,而它里面还并没有HandlerMethodReturnValueHandler这个接口,所以此处就不介绍了,知道就行。注意版本必须是Spring5以内的,因为Spring5以后那两个过时的类就直接都干掉了~~~

【小家Spring】Spring MVC容器的web九大组件之---HandlerAdapter源码详解---一篇文章带你读懂返回值处理器HandlerMethodReturnValueHandler_第6张图片

请注意,上面原理已经讲过,这里面处理器的先后顺序还是比较重要的~~~从下面源码处也能看出,Spring MVC大致上做了分类

	private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {
		List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>();

		// Single-purpose return value types
		// 目的单纯的返回值处理器(这个一般都和视图解析器有关,当然还有异步~)
		handlers.add(new ModelAndViewMethodReturnValueHandler());
		handlers.add(new ModelMethodProcessor());
		handlers.add(new ViewMethodReturnValueHandler());
		handlers.add(new ResponseBodyEmitterReturnValueHandler(getMessageConverters(),
				this.reactiveAdapterRegistry, this.taskExecutor, this.contentNegotiationManager));
		handlers.add(new StreamingResponseBodyReturnValueHandler());
		handlers.add(new HttpEntityMethodProcessor(getMessageConverters(),
				this.contentNegotiationManager, this.requestResponseBodyAdvice));
		handlers.add(new HttpHeadersReturnValueHandler());
		handlers.add(new CallableMethodReturnValueHandler());
		handlers.add(new DeferredResultMethodReturnValueHandler());
		handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory));

		// Annotation-based return value types
		// 基于注解的返回值处理器:@ModelAttribute和@ResponseBody
		handlers.add(new ModelAttributeMethodProcessor(false));
		handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(),
				this.contentNegotiationManager, this.requestResponseBodyAdvice));

		// Multi-purpose return value types
		// 多值返回处理器  这两个其实相对稍微复杂点,功能强大点
		handlers.add(new ViewNameMethodReturnValueHandler());
		handlers.add(new MapMethodProcessor());

		// Custom return value types
		// 用户自定义的处理器们~~~~顺序是非常靠后的哟~
		if (getCustomReturnValueHandlers() != null) {
			handlers.addAll(getCustomReturnValueHandlers());
		}

		// Catch-all:处理所有
		// Spring MVC相当于它定位成自己是能够处理所有的请求的~~~~
		// 特别是ModelAndViewResolverMethodReturnValueHandler,我们上面也有举例了
		if (!CollectionUtils.isEmpty(getModelAndViewResolvers())) {
			handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers()));
		}
		else {
			handlers.add(new ModelAttributeMethodProcessor(true));
		}

		return handlers;
	}

备注:若遇上多个处理器都能处理器的情况下,是按照添加顺序执行的。比如JacksonFastJson都能处理,那就根据添加顺序了,最终生效的肯定只有一个

总结

Spring MVC支持各种返回值类型,是因为默认给我们注册了足够锁的返回值处理器。它面向接口编程以及对责任链模式很好的使用,实现了非常高的扩展性和解耦性。

一个成熟的框架,体现在它对很多细节上的处理,这才行程了一个产品,而Spring Framework就是这么样一个很优秀的产品,值得参考、学习


关注A哥

Author A哥(YourBatman)
个人站点 www.yourbatman.cn
E-mail [email protected]
微 信 fsx641385712
活跃平台
公众号 BAT的乌托邦(ID:BAT-utopia)
知识星球 BAT的乌托邦
每日文章推荐 每日文章推荐

你可能感兴趣的:(#,享学Spring,MVC)