【原創】遨遊springmvc之HandlerMethodReturnValueHandler

1.前言

在springmvc中,不少人都知道@ResponseBody,加了它那就會返回對應的數據結果(json),而不是一張jsp或者其餘視圖。若是不加,那麼它就返回了一個具體的視圖,如jsp。那麼讓咱們來深刻探析下這裏面的究竟。java

在前一篇咱們主要講述了springmvc對傳入參數的接口HandlerMethodArgumentResolver。那麼springmvc一樣也也提供了一系列對響應返回值進行處理的接口,核心接口類就是本篇要介紹的HandlerMethodReturnValueHandler。web

2.原理

2.1 接口說明

public interface HandlerMethodReturnValueHandler {

	/**
	 * Whether the given {@linkplain MethodParameter method return type} is
	 * supported by this handler.
	 * @param returnType the method return type to check
	 * @return {@code true} if this handler supports the supplied return type;
	 * {@code false} otherwise
	 */
	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.
	 * @param returnValue the value returned from the handler method
	 * @param returnType the type of the return value. This type must have
	 * previously been passed to {@link #supportsReturnType} which must
	 * have returned {@code true}.
	 * @param mavContainer the ModelAndViewContainer for the current request
	 * @param webRequest the current request
	 * @throws Exception if the return value handling results in an error
	 */
	void handleReturnValue(Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;

}

HandlerMethodReturnValueHandler是在spring3.1以後引入的spring

它主要包含2個方法:json

1.supportsReturnType()決定了哪類類型的返回值將啓用該返回值處理器mvc

2.handleReturnValue則是主要處理返回值的處理邏輯,而且將處理好的值返回給model,還能夠處理該返回什麼視圖app

 

2.2 依賴

HandlerMethodReturnValueHandler的實現大多類都是已ReturnValueHandler或者Processor(包含了參數處理)結尾jsp

 

2.3 源碼分析

2.3.1 初始化HandlerMethodReturnValueHandler

在初始化RequestMappingHandlerAdapter時候,springmvc默認初始化了一系列返回值處理器,而且提供了自動以的HandlerMethodReturnValueHandler的入口。ide

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

		// Single-purpose return value types
		handlers.add(new ModelAndViewMethodReturnValueHandler());
		handlers.add(new ModelMethodProcessor());
		handlers.add(new ViewMethodReturnValueHandler());
		handlers.add(new ResponseBodyEmitterReturnValueHandler(getMessageConverters()));
		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
		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
		if (!CollectionUtils.isEmpty(getModelAndViewResolvers())) {
			handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers()));
		}
		else {
			handlers.add(new ModelAttributeMethodProcessor(true));
		}

		return handlers;
	}

 

HandlerMethodReturnValueHandler的匹配是按照初始化的順序,請看下面table羅列的處理器和對應的處理類型。源碼分析

處理器 處理類型
針對一中類型
ModelAndViewMethodReturnValueHandler ModelAndView
ModelMethodProcessor Model
ViewMethodReturnValueHandler View
ResponseBodyEmitterReturnValueHandler ResponseEntity<ResponseBodyEmitter>
StreamingResponseBodyReturnValueHandler ResponseEntity<StreamingResponseBody>
HttpHeadersReturnValueHandler HttpHeaders
 CallableMethodReturnValueHandler  Callable
DeferredResultMethodReturnValueHandler DeferredResult、ListenableFuture、CompletionStage
AsyncTaskMethodReturnValueHandler WebAsyncTask
針對註解
ModelAttributeMethodProcessor @ModelAttribute(require=false)
RequestResponseBodyMethodProcessor @ResponseBody
處理多種類型
ViewNameMethodReturnValueHandler void、CharSequence(V4.2)
MapMethodProcessor Map
自定義返回值處理器
ModelAndViewResolverMethodReturnValueHandler 默認處理,若是以上的都不知足就會進入
ModelAttributeMethodProcessor @ModelAttribute(require=true)

 

2.3.2 RequestResponseBodyMethodProcessor

public boolean supportsReturnType(MethodParameter returnType) {
//支持類型
		return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
				returnType.hasMethodAnnotation(ResponseBody.class));
	}

mavContainer.setRequestHandled(true);//請求是否已經徹底在處理程序中處理好,若是是fasle,則繼續流轉到對應的視圖,若是設置爲true,則不會再進過視圖這一 層直接響應,默認是false。ui

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

		mavContainer.setRequestHandled(true);//若是沒有視圖,則必須設置爲true,不然會返回視圖層
		ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
		ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);

		// Try even with null return value. ResponseBodyAdvice could get involved.
        //輸出
		writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
	}

 

3.實例

咱們來實現一個相似@ResponseBody的功能。

3.1 KingsResponseBody

@Target ( {ElementType.TYPE, ElementType.METHOD})
@Retention (RetentionPolicy.RUNTIME)
@Documented
public @interface KingsResponseBody {
    String type() default "json";
}

 

3.2 CustomerHandlerMethodReturnValueHandler

public class CustomerHandlerMethodReturnValueHandler implements HandlerMethodReturnValueHandler {
    @Override
    public boolean supportsReturnType(MethodParameter returnType) {
        return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), KingsResponseBody.class) || returnType.hasMethodAnnotation(KingsResponseBody.class));
    }
    
    @Override
    public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
        KingsResponseBody anno = returnType.getMethodAnnotation(KingsResponseBody.class);
        mavContainer.setRequestHandled(true);
        String type = anno.type();
        HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
        response.setContentType("text/json;charset=UTF-8");
        try (PrintWriter out = response.getWriter()) {
            Gson jb = new Gson();
            out.write(jb.toJson(returnValue));
            out.flush();
        } catch (IOException e) {
            throw e;
        }
    }
}

有時候咱們還能夠往響應頭裏面丟一些內容什麼的,具體的實現根據需求而定。

 

3.3 配置

<mvc:annotation-driven >
        <mvc:return-value-handlers>
            <bean class="com.kings.template.mvc.CustomerHandlerMethodReturnValueHandler"/>
        </mvc:return-value-handlers>
    </mvc:annotation-driven>

 

3.4 控制器

@Controller
public class HandlerMethodReturnValueHandlerDemoController {
    
    @RequestMapping (value="/returnvalue/1",method=GET)
    @KingsResponseBody
    public List<Person> demo() {
        Person p1 = new Person();
        p1.setName("WS");
        Person p2 = new Person();
        p2.setName("Kings");
        return Lists.newArrayList(p1,p2);
    }
}

 

訪問:http://localhost:8080/returnvalue/1

返回:

[{"name":"WS"},{"name":"Kings"}]

 

4. 總結

當咱們須要統一處理springmvc返回值的時候咱們就能夠考慮使用HandlerMethodReturnValueHandler,並且也很是簡單,好比某些場景會須要咱們在響應裏面加入特定一些什麼響應頭或者須要轉化爲指定格式。

 

發現一個機制的導航😳

相關文章
相關標籤/搜索