深入分析 RestController 與 Controller 的區別,你真的瞭解嗎?

語言: CN / TW / HK

關注公眾號:IT老哥,每天閲讀一篇乾貨技術文章,一年後你會發現一個不一樣的自己。

@RestController和@Controller註解

我們都知道RestController默認都只提供Rest風格接口返回值,針對不需要返回頁面的Controller都採用RestController進行註解,下面根據源碼簡單分析一下兩者處理上的區別。

@RestController源碼如下。

@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Controller@ResponseBodypublic @interface RestController {   /**    * The value may indicate a suggestion for a logical component name,    * to be turned into a Spring bean in case of an autodetected component.    * @return the suggested component name, if any    * @since 4.0.1    */   String value() default "";}

@RestController的編寫方式依賴註解組合,@RestController@Controller@ResponseBody標註,表示@RestController具有兩者的註解語義,因此在註解處理時@RestController@Controller多具有一個@ResponseBody語義,這就是@RestController@Controller的區別,也是@RestController的返回值為何都是經過轉換的json的原因。

所以小結就是:@RestController = @Controller + @ResponseBody;

@ResponseBody註解的處理過程

既然知道@RestController@Controller的區別是多了一個@ResponseBody語義,我們不妨瞭解一下@ResponseBody的處理過程。

首先,可以知道,@ResponseBody是一個針對方法返回值進行處理的註解。如果熟悉Spring MVC處理過程的話,可以知道在根據requesturl映射獲取到HandlerMethod之後,根據HandlerMethod調度請求方法的對象是HandlerAdapter,方法調用結束,返回值處理的調度對象也是HandlerAdapter。所以,@ResponseBody註解的處理應該也是在HandlerAdapter中完成。

RequestMappingHandlerAdapter#invokeHandlerMethod方法裏面,有下面幾句比較重要的代碼

//創建方法調用對象ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);//......//設置返回值處理器invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);//......//調用方法invocableMethod.invokeAndHandle(webRequest, mavContainer);

returnValueHandlers這個變量聽名字就像返回值處理,實際上也是對返回值處理的處理器集合。首先創建一個調用方法的對象,然後注入處理器,最後調用方法,這就是完整的一個流程。

我們可以再分析一下這裏面的處理器初始化時有沒有我們的針對@ResponseBody註解的處理器。

@ResponseBody註解處理器初始化

搜索一下returnValueHandlers初始化的地方,可以看到是這麼個調用鏈:

  • RequestMappingHandlerAdapter#afterPropertiesSet

  • handlers = RequestMappingHandlerAdapter#getDefaultReturnValueHandlers

returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers)

所以是在RequestMappingHandlerAdapter的bean初始化完成時,就會進行返回值處理器的初始化,在RequestMappingHandlerAdapter#getDefaultReturnValueHandlers方法內部執行,代碼如下。

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));   //@ResponseBody註解處理器   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;}

可以看到非常對處理器,RequestResponseBodyMethodProcessor就是@ResponseBody的處理器。

@ResponseBody註解處理器調用

進入調用方法invocableMethod.invokeAndHandle(webRequest, mavContainer)/ServletInvocableHandlerMethod#invokeAndHandle,繼續進行調用,跟蹤調用鏈如下。

  • ServletInvocableHandlerMethod#invokeAndHandle

  • this.returnValueHandlers.handleReturnValue/HandlerMethodReturnValueHandlerComposite#handleReturnValue

HandlerMethodReturnValueHandlerComposite#handleReturnValue代碼如下所示。

public void handleReturnValue(Object returnValue, MethodParameter returnType,      ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {//選擇一個合適的處理器   HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);   if (handler == null) {      throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());   }   //處理返回值   handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);}

so,基本就是從所有處理器中選出目標處理器,處理返回值。進入HandlerMethodReturnValueHandlerComposite#selectHandler

private HandlerMethodReturnValueHandler selectHandler(Object value, MethodParameter returnType) {    boolean isAsyncValue = isAsyncReturnValue(value, returnType);    for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {        if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {            continue;        }        //判斷處理器是否支持        if (handler.supportsReturnType(returnType)) {            return handler;        }    }    return null;}

RequestResponseBodyMethodProcessor#supportsReturnType,代碼如下。

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

明顯如果類上有@ResponseBody或者方法上有的話,就能適配處理器,@RestController具有@ResponseBody語義能夠適配,所以進行RequestResponseBodyMethodProcessor#handleReturnValue進行返回值處理。

關注公眾號:IT老哥,每天閲讀一篇乾貨技術文章,一年後你會發現一個不一樣的自己。