Java 自定義註解

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());
            }
        }
    }
}

運行截圖如下所示,可以發現獲取註解的屬性也不是有序的。

參考文獻:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章