[spring注解]Spring相關注解(五)

前言

??本篇文章主要簡單了解下Spring中一些JSR規范所提供的注解,所謂JSR規范,是Java Specification Requests的縮寫,意為Java規范提案,它是JCP,Java標準化組織(Java Community Process)提交給Sun/Oracle的標準化技術規范的請求,目的是為了提供一種規范,并且補充和完善JAVA相關的功能。而目前,JSR規范已經成為了Java界的一個重要標準。JSR所有規范的地址是:https://jcp.org/en/jsr/all

一、JSR-250規范相關
1. Resource注解

??和@Autowired功能類似,@Resource注解也是用來注入bean的,但它并不屬于Spring的注解,而是JSR-250規范中提供的注解,位于javax.annotation包下,它和@Autowired不同的一點主要就是,默認情況下它是根據名稱(byName)來注入的,而@Autowired是根據類型來注入的(byType)。我們先來看下它的實現:

@Target({TYPE, FIELD, METHOD})
@Retention(RUNTIME)
public @interface Resource {

從上面我們可以看到,該注解可以用于類,方法與屬性上。接下來,我們來看一下它的一些參數:

  1. name,用于綁定JNDI context的對象的名稱(不太明白),默認情況下,用于屬性上時,該名稱是字段名;用于方法上時,名稱是與方法相對應的JavaBean的屬性名;而用于類上時,沒有默認值,必須要指定一個。
@Resource
private DataSource dataSource; // bean的名稱就是dataSource

// 同理
private DataSource dataSource;
@Resource
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
  1. lookup,要注入的bean的名稱(不太明白);
  2. type,要注入的bean的類型,和name的默認情況類似。用于屬性上時,該類型是屬性字段的類型;用于方法上時,類型是與方法相對應的javaBean的屬性類型;而用于類上時,沒有默認值,必須要指定一個;
  3. authenticationType, 枚舉類型,不太明白;
  4. shareable,表示該組件和其他組件之間是否可以共享該資源,默認true類型;
  5. mappedName,不太明白;
  6. description,要注入的bean的描述信息。

由于Resource注解不但但用于bean的注入,更多的情況下還可以用于解析JNDI相關的對象,所以我們不用過多關注它的屬性,等用到JNDI的時候再來查看文檔不晚。

@Resource在通過byName 進行自動注入對象時,如果查詢不到,會回退到byType,也就是說如果通過byName查詢不到對象,那么就會通過byType來進行注入。

有關@Resource的解釋,除了看下Spring官方文檔,可以看下Oracle 的JAVA EE 6的官方文檔:https://docs.oracle.com/javaee/6/tutorial/doc/bncjk.html

2. PostContruct 和 PreDestroy 注解

??首先,我們知道,Spring 容器中的 Bean 是有生命周期的,Spring 允許在 Bean 在初始化完成后以及 Bean 銷毀前執行特定的操作,而常用的設定方式有以下三種:

1.通過實現 InitializingBean/DisposableBean 接口來定制初始化之后或者銷毀之前的操作方法;

  1. 通過 <bean> 元素的 init-method/destroy-method屬性指定初始化之后 或者銷毀之前調用的操作方法;
  2. 在指定方法上加上@PostConstruct 或@PreDestroy注解來制定該方法是在初始化之后還是銷毀之前調用。

??和@Resource注解一樣,這兩個注解也不屬于Spring本身的注解,也是屬于JSR-250規范所提供的注解,而且這兩個注解都是用于方法級別的。

這里有關它們的順序,我們簡單說下,感興趣的童鞋可以自己測試下:

在Spring的bean進行注入的時候,首先會初始化構造方法,然后會執行@PostContruct注解修飾的方法,其次會執行InitializingBean方法,最后,才會執行init-method方法。也就是 Constructor > @PostConstruct > InitializingBean > init-method;而對應的PreDestroy修飾的方法則會在destroy方法執行之后執行;

這里參考自:源碼解析:init-method、@PostConstruct、afterPropertiesSet孰先孰后

  1. 這兩個注解所修飾的方法一般是無參的非靜態方法,
  2. 由于@PostConstruct注解是在對象構造完成后調用init-method之前執行的,所以如果我們配置了延遲初始化的話(default-lazy-init="true"),只有當這個bean被調用了的情況下,@PostConstruct注解所修飾的方法才會執行。

二、JSR-330 規范

1. Inject 注解

??另一個可以實現注入功能的注解是@Inject,該注解也不是Spring注解,而是JSR-330規范中提供的注解,而JSR-330規范則是為了統一Java依賴注入的規范,所以其核心注解@Inject基本上可以完全替換@Autowired注解。該規范一共6個注解,分別對應為:InjectNamedProviderQualifierScopeSingleton。該注解所在jar包對應的maven地址為:

<dependency>
    <groupId>javax.inject</groupId>
    <artifactId>javax.inject</artifactId>
    <version>1</version>
</dependency>
  1. @Inject 和 @Autowired一樣,也是通過byType來自動注入,都是通過AutowiredAnnotationBeanPostProcessor類來實現的依賴注入。@Inject 注解可以用于方法,屬性,構造器上,與@Autowired不同的是,@Inject沒有 required屬性,因此@Inject注解所依賴的關系必須存在,如果不存在,則會拋出一個異常。
  2. 相對于@Autowired注解使用@Qualifier注解,@Inject使用@Named注解來解決@Autowired的這個問題。而@Named注解與@Qualifier注解又是特別相似,兩者的區別僅僅在于語義層面。@Qualifier注解幫助我們縮小所匹配Bean的選擇范圍(默認使用Bean的ID),而@Named通過Bean的ID來標志可選擇的bean。
@Inject
@Named("userService")
private UserService userService;
  1. JSR-330在 javax.inject包里有自己的@Qualifier注解,但JSR-330不建議使用該注解,而是鼓勵我們將此注解做為元注解來自定義我們的注解,另外,這兩個@Qualifier注解功能基本上是相同的。

JSR-330的這幾個注解我們就不仔細說了,等用到了再來細說。對應的官網文檔地址:https://docs.spring.io/spring/docs/4.3.14.RELEASE/spring-framework-reference/html/beans.html#beans-standard-annotations

三、JSR-303 相關注解

1. Valid注解

??首先,Valid不是Spring本身提供的注解,而是由JSR-303用于校驗傳入參數合法性的一個注解。做Java后臺開發的都知道,前端參數合法性校驗是我們開發過程中必不可少的步驟,但是驗證參數本身算是一個體力活,而且還會造成代碼冗余,影響代碼的美觀性,而JSR-303則正是為了解決這個問題而出現的一種比較優雅的解決方式。

JSR-303作為JAVA-EE 6 的一項規范,又被稱為Bean Validation,而Hibernate Validator則是官方的參考實現,并且Hibernate Validator還在JSR-303的基礎上進行了擴展,以支持更多的校驗規則。

??第一步,首先,我們需要引入validation-api jar包和hibernate-validator包,我們可以去Maven倉庫下載最新版的jar包,截至目前,最新版的包分別是:

<!-- https://mvnrepository.com/artifact/javax.validation/validation-api -->
<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <version>2.0.1.Final</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-validator -->
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.0.9.Final</version>
</dependency>

第二步,編寫我們的實體對象User:

public class User implements Serializable{
    @NotNull(message = "名字不能為空")
    private String name;
    @NotNull(message = "id不能為空")
    private String id;
    @Min(value = 14, message = "數字不能小于14")
    private Double aDouble;
    ......
}

第三步,編寫我們的控制器方法,使用@Valid注解來進行校驗:

@RequestMapping(value = "/test.html", method = RequestMethod.GET)
public JsonResult findPet(@Valid User user, BindingResult bindingResult) {
    if (bindingResult.hasErrors()) {
        // 錯誤提示信息
        List<ObjectError> errorList = bindingResult.getAllErrors();
        for(ObjectError error : errorList){
            System.out.println(error.getDefaultMessage());
        }
        FieldError fieldError = bindingResult.getFieldError();
        String validMess = fieldError.getDefaultMessage();
        jsonResult = new JsonResult(false, validMess);
        return jsonResult;
    }
}

??一般情況下,我們可以通過借助BindingResult對象來判斷錯誤信息,然后根據獲取到的錯誤信息,拋給全局異常處理,或者把錯誤信息封裝成 JsonResult 對象返回給我們的前端。

??無需過多配置,就可以實現校驗的功能,并且省去了許多冗余的代碼,讓代碼看上去更美觀些。而如果要查看對應的校驗類型的話,我們可以參考JSR303規范來進行查看。而如果Hibernate Validtor滿足不了我們的需求的時候,我們還可以自定義校驗注解。

這里簡單說幾點:

  1. 一般情況下,我們都可以通過BindingResult來檢查錯誤信息,如果不適用該對象的話,如果校驗不通過,Spring將直接會拋出異常;
  2. JSR-303和Hibernate Validtor都是用于校驗對象,而針對單個參數的校驗,我們可以使用Spring Validator來實現,Spring Validator是在JSR-303規范基礎上進行了擴展,添加了MethodValidationPostProcessor攔截器,可以使用@Validated注解實現對方法參數的校驗,并且還可以實現分組校驗,而@Valid是不支持分組校驗的(比如同一個實現在不同的controller中,而我們想分別對不同Controller中的實體進行校驗,這就是分組),后面看下簡單的例子。
  3. 這里只列舉了JSR-303規范,并且只學習了最基礎也是最常用的校驗功能,其實關于校驗的東西還有很多,比如國際化,錯誤信息封裝,組合參數校驗等,這些等我們后續有時間了再仔細研究。

針對方法參數驗證,我們看一個簡單的實現:

    1. 添加MethodValidationPostProcessor的bean配置:
@Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
    return new MethodValidationPostProcessor();
}
    1. Controller添加@Validated注解:
@RestController
@RequestMapping("/world")
@Validated
public class WorldController {
}
    1. Controller中方法接著使用JSR-303注解:
@RequestMapping(value = "/test.html", method = RequestMethod.GET)
public String findPet(@NotNull(message = "不能為空") String user) {
}
    1. 可以看到我們的配置已經生效,如果我們的傳入參數不滿足條件,將會拋出ConstraintViolationException異常,我們可以通過它的getConstraintViolations獲得所有沒有通過校驗的ConstraintViolation集合,然后來得到它們對應的錯誤信息;也可以使用全局異常來處理:
@ExceptionHandler
@ResponseBody
public JsonResult exceptions(ConstraintViolationException exception) {
    JsonResult jsonResult = new JsonResult();
    jsonResult.setSuccess(false);
    jsonResult.setMessage(exception.getMessage());
    return jsonResult;
}
  1. JSR-303不但可以用于后端接收前端參數的校驗,也可以用于dubbo接口參數的校驗;
  2. 這里大致說下相關版本對應的規范,Bean Validation 1.0對應 JSR-303,屬于JAVA EE 6的規范;Bean Validation 1.1 對應 JSR-349,屬于JAVA EE 7的規范;Bean Validation 2.0對應JSR-380,屬于JAVA EE 8的規范。大家可以根據需要,了解下新版本的內容。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,619評論 6 539
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,155評論 3 425
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,635評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,539評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,255評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,646評論 1 326
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,655評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,838評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,399評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,146評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,338評論 1 372
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,893評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,565評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,983評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,257評論 1 292
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,059評論 3 397
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,296評論 2 376

推薦閱讀更多精彩內容