Spring MVC九大组件之HandlerAdapter如何处理返回值(HandlerMethodReturnValueHandler)

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支持的返回值类型众多,而我们绝大部分情况下是无需自己自定义返回值处理器的,下面我们可以看看它的继承树:

在这里插入图片描述

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,所以访问的结果如下:

在这里插入图片描述

原因就是你没有viewName,SpringMVC采用了默认的获取viewName的机制(还是hello),所以进行转发的时候发现是相同的进入死循环,就抛错了~~~

在这里插入图片描述

走了一个默认获取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 requestMappingHandlerAdapt    
    @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<HandlerMethodReturnValueHandler> 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能够生效,因为我们已经实现了偷天换日~~~:

在这里插入图片描述

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

请求一下,浏览器没有任何内容。但是控制台里可以看到如下:

在这里插入图片描述

这个处理器可以帮助我们在需要对请求头进行特殊处理的时候,进行一定程度的加工。它是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()方法是受保护的。因此我们可以通过重新注册一个它来达到效果:

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

@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留给我们处理自定义类型的一个钩子,可以这么来用~~~

ModelAndViewResolver从setModelAndViewResolvers()的javadoc里可以看出,它一般用于来做向下兼容。如果你要自定义,一般需要重写HandlerMethodReturnValueHandler和ModelAndViewResolver

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

ServletModelAttributeMethodProcessor

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

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

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<T>
    // 当然你也可以直接使用HttpEntity<T> 作为返回值也是可以的
    @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更加的便捷~~~~

AbstractMessageConverterMethodProcessor

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

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

其实它有一个非常重要的方法: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这个子接口,你的排名就会靠前执行了~~~

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,也会处理返回值类型为ListenableFuture和CompletionStage(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

它最终也是转换为一个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。

它用在RequestMappingHandlerAdapter和ExceptionHandlerExceptionResolver里,此处以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以后那两个过时的类就直接都干掉了。

在这里插入图片描述

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

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

总结

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

 

参考:

 

posted @ 2021-12-12 10:07  残城碎梦  阅读(308)  评论(0编辑  收藏  举报