1. 程式人生 > >深入分析RestController與Controller

深入分析RestController與Controller

@RestController和@Controller註解

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

@RestController原始碼如下。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @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中完成。(ps:不清楚Spring MVC可以看

深入分析Spring MVC

在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進行返回值處理。