@Controller和@RestController源碼解析

2018年不知不覺已經走到了尾聲,你還在爲分不清@Controller和@Restcontroller而煩惱嗎?這篇博文從源碼層面分析這兩個註解,值得一讀。html

首先貼一張源碼的圖,對比一下,左邊是@Controller的源碼,右邊是@RestController的。編程

若是以爲不清楚,看下面代碼:json

@Controller:瀏覽器

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
     /**
      * 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 (or empty  String otherwise)
      */
     @AliasFor(annotation = Component.class)
     String value() default "";
}
View Code

@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 (or empty  String otherwise)
      * @since 4.0.1
      */
     @AliasFor(annotation = Controller.class)
     String value() default "";

}
View Code

顯然,兩個註解的源碼裏面都包含許多的註解:app

@Controller的註解包括:@Target({ElementType.TYPE})、@Retention(RetentionPolicy.RUNTIME)、@Documented、@Componentide

@RestController的註解包括:@Target(ElementType.TYPE)、@Retention(RetentionPolicy.RUNTIME)、@Documented、@Controller、@ResponseBodythis

因此,源碼的分析也就是對註解的分析。spa

內置註解和元註解

註解(也被稱爲元數據)爲咱們在代碼中添加信息提供了一種形式化的方法,使咱們能夠在稍後某個時刻很是方便地使用這些數據。code

Java SE5中有三種內置註解

@Override
表示當前的方法定義將覆蓋超類中的方法
@Deprecated
若是程序中使用了註解爲它的元素,那麼編譯器會發出警告
@SuppressWarnings
關閉不當的編譯器警告信息
 
元註解是專門負責註解其餘的註解,Java內置了四種元註解,專門負責新註解的建立,直接看這張表格(摘自Java編程思想):
@Target
表示該註解能夠用於什麼地方。可能的ElementType參數包括:
CONSTRUCTOR:構造器的聲明
FIELD:域聲明(包括enum實例)
LOCAL_VARIABLE:局部變量聲明
METHOD:方法聲明
PACKAGE:包聲明
PARAMETER:參數聲明
TYPE:類、接口(包括註解類型)和enum聲明
@Retention
表示須要在什麼級別保存該註解信息。可選的RetentionPolicy參數包括:
SOURCE:註解將在編譯器丟棄
CLASS:註解在class文件中可用,但會被VM丟棄
RUNTIME:VM將在運行期也保留註解,所以能夠經過反射機制讀取註解的信息
@Documented 將此註解包含在Javadoc中
@Inherited 容許子類繼承父類中的註解

如今,明白了@Target({ElementType.TYPE})、@Retention(RetentionPolicy.RUNTIME)、@Documented的做用,咱們也能夠自定義一個註解@Algorithms:

/**
*
* Define a annotation named Algorithms
*
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface Algorithms {
    String value() default "";
}

@Component註解

@Component註解源碼以下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface Component {

     /**
      * 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 (or empty  String otherwise)
      */
     String value() default "";

}

加了@Component註解,代表這是一個邏輯組件,告知Spring要爲它建立bean。至關於xml配置文件中的 <bean id="" class=""/>的做用。

@AliasFor註解

@AliasFor註解是一個用於聲明註解屬性別名的註解,源碼以下:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface AliasFor {

     /**
      * Alias for {@link #attribute}.
      * <p>Intended to be used instead of {@link #attribute}  when {@link #annotation}
      * is not declared &mdash; for example: {@code  @AliasFor("value")} instead of
      * {@code @AliasFor(attribute = "value")}.
      */
     @AliasFor("attribute")
     String value() default "";
     /**
      * The name of the attribute that <em>this</em> attribute  is an alias for.
      * @see #value
      */
     @AliasFor("value")
     String attribute() default "";
     /**
      * The type of annotation in which the aliased {@link  #attribute} is declared.
      * <p>Defaults to {@link Annotation}, implying that the  aliased attribute is
      * declared in the same annotation as <em>this</em>  attribute.
      */
     Class<? extends Annotation> annotation() default  Annotation.class;

}

@Controller中的@AliasFor(annotation = Component.class)說明@Controller是Component的一個別名,本質上仍是一個Component,正如註釋中所說「to be turned into a Spring bean in case of an  autodetected component.」,能夠被掃描成一個bean。

同理,@RestController中的@AliasFor(annotation = Controller.class)說明@RestController是Controller的一個別名,是一個Controller,再本質一點說,是個Component,是個Spring bean。

@ResponseBody註解

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ResponseBody {

}

提到@ResponseBody註解,就不得不提一個名詞:表述性狀態轉移(Representational State Transfer,REST)。

什麼是表述性狀態轉移呢?拆開來看:

表述性:REST資源實際上能夠用各類形式來表述,包括JSON、XML、HTML等;

狀態:使用TEST的時候,咱們更關注資源的狀態而不是對資源採起的行爲;

轉移:以某種形式(例如JSON)從一個應用轉換到另外一個應用,例如從服務器到客戶端。

簡單講,REST就是將資源的狀態以最適合客戶端或者服務器的形式從服務器轉移到客戶端(或者反過來)。

在Spring 4.0版本中,Spring支持藉助@ResponseBody註解和各類HttpMethodConverter,替換基於視圖的渲染方式,實現對REST的支持。固然Spring對REST的支持遠不止這一種方式。

@ResponseBody註解告知Spring,要將返回的對象做爲資源發送給客戶端。這個過程跳過正常的模型/視圖流程中視圖解析的過程,而是使用Spring自帶的各類Http消息轉換器將控制器產生的數據轉換爲客戶端須要的表述形式。若是客戶端請求頭的Accept代表他能接受「application/json」,而且Jackson JSON在類路徑下面,那麼處理方法返回的對象將交給MappingJacksonHTTPMessageConverter,並由他轉換爲JSON返回給客戶端;若是客戶端想要「text/html」格式,那麼Jaxb2RootElementHttpMessageConverter將會爲客戶端產生XML響應。

當處理請求時候,@ResponseBody和@RequestBody是啓用消息轉換的一種簡介和強大方式。可是,若是控制器裏面的每一個方法都須要信息轉換功能的話,那麼這些註解就會帶有必定程度的重複性。

因此,Spring 4.0引入了@RestController註解,若是在控制器類上面使用@RestController註解,咱們沒必要再爲每一個方法添加@ResponseBody註解,由於Spring會爲該控制器下面的全部方法應用消息轉換功能。這也是這個Controller之因此叫RestController的緣由,正所謂見名知意。

總結

@RestController至關於@ResponseBody + @Controller一塊兒做用。

若是控制器產生的結果但願讓人看到,那麼它產生的模型數據須要渲染到視圖中,從而能夠展現到瀏覽器中,使用@Controller。

若是控制器產生的結果不須要讓人看到,那麼它產生的數據通過消息轉換器直接返回到瀏覽器,使用@RestController。

參考文獻:

[1] Bruce Eckel. Java編程思想(第四版)[M]. 陳昊鵬譯. 北京:機械工業出版社,2007.

[2] Craig Walls. Spring實戰(第4版)[M]. 張衛濱譯. 北京:人民郵電出版社,2016.

相關文章
相關標籤/搜索