- Spring快速入門
- 崔彥威 盧欣欣 王倩
- 2577字
- 2020-06-29 17:30:38
1.1 注解
本節首先介紹注解的基本概念,理解什么是注解、注解的作用是什么。在此基礎上通過示例動手操作加深理解。
1.1.1 什么是注解
我們先看官方解釋:它提供了一種安全的類似注釋的機制,用來將任何的信息或元數據(metadata)與程序元素(類、方法、成員變量等)進行關聯。為程序的元素(類、方法、成員變量)加上更直觀、更明了的說明,這些說明信息與程序的業務邏輯無關,并且供指定的工具或框架使用。Annontation像一種修飾符一樣,應用于包、類型、構造方法、方法、成員變量、參數及本地變量的聲明語句中。Java注解是附加在代碼中的一些元信息,便于一些工具在編譯、運行時進行解析和使用,起到說明、配置的功能。注解不會也不能影響代碼的實際邏輯,僅僅起到輔助性的作用,包含在java.lang.annotation包中。
看著上面的解釋是不是還是一頭霧水?其實我們可以更通俗地理解一下。最近幾年出現一個詞“斜杠青年”,還有黃某某拍攝的廣告語:給人貼標簽、下定義,總是很容易,而我卻不會因為一件事被定性。這里的斜杠青年、貼標簽都是把某些屬性附加給對象,和注解功能差不多,它提供了一種安全的類似注釋的機制,用來將任何信息或元數據(metadata)與程序元素(類、方法、成員變量等)進行關聯。我們可以再來理解一下這句話,這里的程序元素可以理解為人,信息或元數據理解為標簽,把標簽屬性(信息或元數據)賦給人(程序元素)。
上面兩段基本把什么注解解釋出來了,如果還是不知道注解是什么,那也沒關系。其實我們在編程中已經用到或者看到過了,比如@Override、@Deprecated。是不是很熟悉?其實它們就是注解。
1.1.2 內置注解
上面的@Override、@Deprecated都是Java中內置的注解,除了這兩個還有其他的內置注解。這里列舉了幾個常用的內置注解以及它們的作用。
● @Deprecated:編譯器在編譯階段遇到這個注解時會發出提醒警告,告訴開發者正在調用一個過時的元素,比如過時的方法、過時的類、過時的成員變量。
● @Override:提示子類要復寫父類中被@Override修飾的方法。
● @SuppressWarnings:阻止警告的意思。調用被@Deprecated注解的方法后,編譯器會警告提醒,而有時候開發者會忽略這種警告,他們可以在調用的地方通過@SuppressWarnings:達到目的。
● @SafeVarargs:參數安全類型注解。它的目的是提醒開發者不要用參數做一些不安全的操作,它的存在會阻止編譯器產生unchecked這樣的警告。它是在Java 1.7的版本中加入的。
● @FunctionalInterface:函數式接口注解,這個是Java 1.8版本引入的新特性。函數式編程很火,所以Java 8也及時添加了這個特性。函數式接口(Functional Interface)就是一個具有一個方法的普通接口。
1.1.3 元注解
通過前面的兩小節,我們應該對注解有了一定的認識,下面進一步地了解一下注解。我們在自定義注解時會出現圖1-1所示的一些選項。

圖1-1
這些選項@Retention、@Target、@Documented其實就是元注解。在創建時配置這些元注解,我們也可以推斷出元注解的作用是什么。元注解負責注解自定義注解。java.lang.annotation提供了5種元注解,專門注解其他的注解:
● @Retention:什么時候使用該注解。
● @Target:注解用于什么地方。
● @Documented:注解是否將包含在JavaDoc中。
● @Inherited:是否允許子類繼承該注解。
● @Repeatable:指定注解可重復使用。
1.@Retention定義注解的生命周期
● RetentionPolicy.SOURCE:在編譯階段丟棄。這些注解在編譯結束之后不再有任何意義,所以它們不會寫入字節碼。@Override和@SuppressWarnings都屬于這類注解。
● RetentionPolicy.CLASS:在類加載的時候丟棄。在字節碼文件的處理中有用。注解默認使用這種方式。
● RetentionPolicy.RUNTIME:始終不會丟棄,運行期也保留該注解,因此可以使用反射機制讀取該注解的信息。我們自定義的注解通常使用這種方式。
2.@Target表示注解用于什么地方
默認值為任何元素,表示該注解用于什么地方??捎玫腅lementType參數包括:
● ElementType.CONSTRUCTOR:用于描述構造器。
● ElementType.FIELD:成員變量、對象、屬性(包括enum實例)。
● ElementType.LOCAL_VARIABLE:用于描述局部變量。
● ElementType.METHOD:用于描述方法。
● ElementType.PACKAGE:用于描述包。
● ElementType.PARAMETER:用于描述參數。
● ElementType.TYPE:用于描述類、接口(包括注解類型)或enum聲明。
3.@Documented是一個簡單的Annotations標記注解
表示是否將注解信息添加在Java文檔中。
4.@Inherited定義注解和子類的關系
@Inherited元注解是一個標記注解,闡述了某個被標注的類型是被繼承的。如果一個使用了@Inherited修飾的annotation類型被用于一個class,那么這個annotation將被用于該class的子類。
5.@Repeatable指定注解可重復使用
使用@Repeatable修飾表示該注解可以為重復使用。
1.1.4 自定義注解
元注解是負責注解自定義注解的。自定義注解時是有一些規則限制的,具體如下:
● Annotation型定義為@interface,所有的Annotation會自動繼承java.lang.Annotation這一接口,并且不能再去繼承別的類或是接口。
● 參數成員只能用public或默認(default)這兩個訪問權修飾。
● 參數成員只能用基本類型byte、short、char、int、long、float、double、boolean八種基本數據類型和String、Enum、Class、annotations等數據類型,以及這一些類型的數組。
● 要獲取類方法和字段的注解信息,必須通過Java的反射技術來獲取Annotation對象,因為除此之外沒有其他獲取注解對象的方法。
● 注解也可以沒有定義成員。
我們這里自定義一個注解來練習一下,主要用來演示自定義注解以及注解的繼承。
1.定義CustomDescription注解
CustomDescription注解相當于標簽。為了能多貼標簽,又定義了注解容器CustomDescriptions。其中,@Retention(RUNTIME)表示在運行時環境也可以獲取注解,@Inherited表示可繼承,@Repeatable(CustomDescriptions.class)表示該注解可多次使用。
package CusAnnontation; import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.RetentionPolicy.RUNTIME; import java.lang.annotation.Documented; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.Target; import java.lang.annotation.Repeatable; @Documented @Retention(RUNTIME) @Target(TYPE) @Inherited @Repeatable(CustomDescriptions.class) public @interface CustomDescription { String description() default ""; }
CustomDescriptions容器:
package CusAnnontation; import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.RetentionPolicy.RUNTIME; import java.lang.annotation.Documented; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.Target; @Documented @Retention(RUNTIME) @Target(TYPE) @Inherited public @interface CustomDescriptions { CustomDescription[] value(); }
2.實現繼承關系
這里為了演示,我們創建了兩個類:一個基類Person,一個子類Student。在Person類加兩個自定義注解,在Student中加一個自定義注解。
Person:

Student:

3.通過反射獲取注解屬性值

這里我們想通過反射(可以先不要理解)獲取Student類的注解值,那么問題來了,它是輸出什么的呢?會輸出“description:學生”嗎?并不是,而是輸出父類Person的注解。
輸出:
description:基類 description:人
如果想輸出子類Student的注解該怎么設置呢?很簡單,只需在子類Student上覆蓋父類的注解就好。
@CustomDescription(description="學生") @CustomDescription(description="人") public class Student extends Person
輸出:
description:學生 description:人
此時輸出的就是子類的注解值了。這里我們還可以驗證@Retention生命周期的作用,只需要把@Retention(RUNTIME)改成CLASS,再運行就會報錯,因為main方法中的custormDescriptions對象是一個null空值。不過自定義注解一般來說都是使用@Retention(RUNTIME)。
1.1.5 注解使用場景介紹
在上一小節通過實例學習了自定義注解的使用,之后就該解決怎么用的問題了。其實,注解應用的場景還是挺多的。
(1)使用注解做bean的屬性值校驗,例如在開發Java服務器端代碼時,會要求對外部傳來的參數合法性進行驗證。hibernate-validator提供了一些常用的參數校驗注解。
(2)使用注解做權限控制。例如,shiro框架中有5個權限注解,我們也可以自定義注解進行權限控制。
(3)代替配置文件功能,像Spring基于注解的配置,減少了xml的配置。
(4)可以生成文檔,像Java代碼注釋中的@see、@param等。
這里只是列舉了幾個使用場景,其實還有很多地方可以使用注解。
- GitLab Cookbook
- 青少年軟件編程基礎與實戰(圖形化編程三級)
- Windows Server 2012 Unified Remote Access Planning and Deployment
- 數據結構(C語言)
- 精通Linux(第2版)
- Android開發:從0到1 (清華開發者書庫)
- Mastering openFrameworks:Creative Coding Demystified
- Orleans:構建高性能分布式Actor服務
- Solutions Architect's Handbook
- Illustrator CS6設計與應用任務教程
- Mastering Object:Oriented Python(Second Edition)
- WCF全面解析
- Splunk Essentials
- Switching to Angular 2
- Implementing DevOps with Ansible 2