1. 元註解
元註解是 Java 提供的一些基本註解,使用這些元註解區可疑創建新的註解;可以先大致看一下元註解,然後去看後面自定義註解的例子。
元註解有 @Retention, @Documented, @Target, @Inherited,@Repeatable 五種。
1.1 @Retention
@Retention 可以定義註解的生命週期,註解的存活時間有如下三種:
- RetentionPolicy.SOURCE: 註解只在源碼階段保留,在編譯器完整編譯之後,它將被丟棄忽視;
例:@Override,源碼如下所示:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
- RetentionPolicy.CLASS: 註解只被保留到編譯進行的時候,註解不會被加載到 JVM 中;
- RetentionPolicy.RUNTIME: 註解可以保留到程序運行的時候,註解會被加載進入到 JVM 中,所以在程序運行時可以獲取到它們。
例: @Service 擁有 @Retention(RetentionPolicy.RUNTIME) 註解,所以在程序運行時可以捕獲到它們。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {
@AliasFor(
annotation = Component.class
)
String value() default "";
}
1.2 @Target
@Target 表示該註解用於什麼地方,可以理解爲:當一個註解被 @Target 註解時,這個註解就被限定了運用的場景。可以使用的 ElementType 參數:
- ElementType.CONSTRUCTOR: 對構造方法進行註解;
- ElementType.ANNOTATION_TYPE: 對註解進行註解;
- ElementType.FIELD: 對屬性、成員變量、成員對象(包括 enum 實例)進行註解;
- ElementType.LOCAL_VARIABLE: 對局部變量進行註解;
- ElementType.METHOD: 對方法進行註解;
- ElementType.PACKAGE: 對包進行註解;
- ElementType.PARAMETER: 對描述參數進行註解;
- ElementType.TYPE: 對類、接口、枚舉進行註解;
@Override 是 ElementType.METHOD,對方法進行註解;
@Service 是 ElementType.TYPE,對類、接口、枚舉進行註解;
1.3 @Documented
@Documented 是一個簡單的標記註解,表示是否將註解信息添加在 Java 文檔,即 Javadoc 中。
1.4 @Inherited
Inherited 是指繼承,@Inherited 定義了一個註釋與子類的關係。如果一個超類帶有 @Inherited 註解,那麼對於該超類,它的子類如果沒有被任何註解應用的話,那麼這個子類就繼承了超類的註解。
1.5 @Repeatable
@Repeatable 是 Java 8 中加入的,是指可重複的意思。通常使用 @Repeatable 的時候指註解的值可以同時取多個。例如:@MapperScan 這個註解。
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({MapperScannerRegistrar.class})
@Repeatable(MapperScans.class)
public @interface MapperScan {
// 略
}
2. 自定義註解
自定義了個 HelloWorld 註解 ,聲明註解使用 @ Interface,如下所示。
public @interface HelloWorld
完整的一個註解,註解有兩個屬性,user 用戶,message 名稱(默認爲helloworld)。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface HelloWorld {
public String user();
public String message() default "helloWorld";
}
在方法上標明註解的例子:
public class Main {
public String user;
public String message;
public Main(String user, String message) {
this.user = user;
this.message = message;
}
@HelloWorld(user="syrdbt", message = "hello world")
void show() {
System.out.println("show");
}
@HelloWorld(user="syrdbt")
void show1() {
System.out.println("show1");
}
@Override
public String toString() {
return "Main{" +
"user='" + user + '\'' +
", message='" + message + '\'' +
'}';
}
}
訪問註解:
public class Test {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
// 傳入字節碼
trackUseCases(Main.class);
}
public static void trackUseCases(Class<?> cl) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
for (Method m : cl.getDeclaredMethods()) {
// 獲得註解的對象
HelloWorld helloWorld = m.getAnnotation(HelloWorld.class);
// 訪問註解上面的屬性
if (helloWorld != null) {
System.out.println("Found Use Case:" + helloWorld.user() + " "
+ helloWorld.message());
}
}
}
}
運行截圖如下所示,可以知道獲取註解的屬性也不是有序的。
3. 使用註解產生一個 bean
這裏使用註解產生一個 bean ,使用從註解的獲取屬性,然後使用反射獲取 Main 類的構造函數,調用構造函數和從註解的獲取屬性 生成 bean 。
public class Test {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
// 傳入字節碼
makeBean(Main.class);
}
public static void makeBean(Class<?> cl) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// 獲取構造函數
Constructor<?> constructor = cl.getConstructor(String.class, String.class);
for (Method m : cl.getDeclaredMethods()) {
//獲得註解的對象
HelloWorld helloWorld = m.getAnnotation(HelloWorld.class);
if (helloWorld != null) {
System.out.println("Found Use Case:" + helloWorld.user() + " "
+ helloWorld.message());
// 使用構造函數生成 bean
Object obj = constructor.newInstance(helloWorld.user(), helloWorld.message());
System.out.println(obj.toString());
}
}
}
}
運行截圖如下所示,可以發現獲取註解的屬性也不是有序的。