博客 / 詳情

返回

Java基礎學須|DK自定義註解

概述

在 Java 開發中,自定義註解(Annotation)是一種元編程機制,廣泛用於框架設計、代碼增強(如 AOP)、參數校驗、權限控制、日誌埋點等場景。大型的框架工具,如Spring、MyBatis、Hibernate 都大量使用註解機制,一些我們熟知的第三方工具(Hutool、Lombok)也都是用了大量的註解機制來增強和擴展應用。

1 關於自定義註解

一般來説,常見的自定義註解基本的語法包括

  • @Target 註解使用的目標(類、方法、字段等)
  • @Retention 保留策略(源碼、類文件、運行時)
  • @Documented 是否包含在 Javadoc 中
  • @Inherited 子類是否可以繼承父類上的註解

自定義註解一般的獲取方式是結合反射讀取註解,而在使用方向上結合 Spring AOP 使用註解是最多的,Spring已經應用在各類Java語言中的架構中。

2 Retention註解

【定義】

@Retention 是一個元註解(Meta‐annotation),來源於java.lang.annotation.Retention,用來標記一個註解自身應當被保留到哪個階段(源碼/字節碼/運行時),他只能用在其他註解的定義上。
一張圖來看下Retention註解RetentionPolicy中的參數。
圖片

【參數詳解】

RetentionPolicy 定義了註解(@interface)在 Java 程序中的保留級別或者説是保留策略,也就是註解信息在什麼階段可見、可用。它包含三個枚舉值SOURCE、CLASS、RUNTIME:

public enum RetentionPolicy {
    SOURCE,
    CLASS,
    RUNTIME
}

SOURCE:註解僅在源代碼中可見,編譯時即被丟棄,生成的 .class 文件和運行時都不可見。CLASS:註解在編譯後保存在 .class 字節碼文件中,但 JVM 加載類時並不保留(即默認行為)。RUNTIME:註解在編譯後保存在 .class 文件中,並且在運行時通過反射依然可訪問。案例測試為了更好的理解Retention註解,可以參看下代碼示例。

package com.liu.aop;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Method;

/**
 * <p> RetentionTest </p>
 * class
 * TODO
 *
 * @author Aion.Liu
 * @version v1.0.0
 * @since 2025/5/21 09:07
 */
public class RetentionTest {

    @SourceOnly
    @ClassOnly
    @RuntimeVisible
    public void test() {
    }

    public static void main(String[] args) throws Exception {
        // 僅 RuntimeVisible 會被反射讀取到
        Method m = RetentionTest.class.getMethod("test");
        System.out.println("SOURCE present: " + m.isAnnotationPresent(SourceOnly.class));
        System.out.println("CLASS present:  " + m.isAnnotationPresent(ClassOnly.class));
        System.out.println("RUNTIME present:" + m.isAnnotationPresent(RuntimeVisible.class));
    }
}


// 僅源碼階段
@Retention(RetentionPolicy.SOURCE)
@interface SourceOnly { }

// 編譯後保留,但運行時不可見
@Retention(RetentionPolicy.CLASS)
@interface ClassOnly { }

// 運行時可見
@Retention(RetentionPolicy.RUNTIME)
@interface RuntimeVisible { }

打印結果如下/xxx/jdk1.8.0_321.jdk/Contents/Home/bin/java -javaagent:/……次數省略不必要的信息。
SOURCE present: false
CLASS present: false

RUNTIME present:true

注意⚠️:
只有標註了 @Retention(RetentionPolicy.RUNTIME) 的註解,才可以通過反射 API(Annotation[], getAnnotation() 等)讀取。

  • 默認策略:如果沒有聲明 @Retention,註解的默認保留策略是 CLASS。
  • 性能考慮:過多 RUNTIME 註解可能增加運行時反射成本,慎用。
  • 兼容性:使用 SOURCE 策略時,IDE 和編譯器插件需同時生效,否則註解處理器可能無法觸發。

3 Target註解

定義@Target 是用來指定自定義註解可以應用於哪些Java 元素的元註解(Meta-annotation),它定義了註解的作用範圍,幫助編譯器在錯誤的地方使用註解時給出提示。一張圖來看下Target註解ElementType中的參數(注意:圖中包括JDK 1.5 - JDK16之間註解參數值)。
圖片

參數詳解

@Target 註解參數是ElementType,他是一個枚舉值,ElementType 定義了所有可用的目標類型,一共12種(截止到JDK21,)。

public enum ElementType {
    TYPE,
    FIELD,
    METHOD,
    PARAMETER,
    CONSTRUCTOR,
    LOCAL_VARIABLE,
    ANNOTATION_TYPE,
    PACKAGE,
    TYPE_PARAMETER,     // JDK 8
    TYPE_USE,                 // JDK 8
    MODULE,                     // JDK 9
    RECORD_COMPONENT; // JDK 16
}

注意⚠️
在使用時可以存在並存的情況,例如 METHOD 和 PARAMETER可以同時應用。案例測試Target註解只能應用在類和方法上面,在字段或參數上使用會編譯報錯。

// 自定義註解
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Audit {
    String action();
}

// 應用在類名稱上
@Audit(action = "UserService")
public class UserService { ... }

public class OrderService {

    // 應用在方法上
    @Audit(action = "createOrder")
    public void create() { ... }
}

4 Documented註解

定義

在 Java 中,@Documented 是一個元註解(meta-annotation),用於指示某個註解類型是否應該被包含在 Javadoc 等生成的文檔中。它是 Java 註解機制的一部分,定義在 java.lang.annotation 包中。

可從源代碼中查看定義,當定義一個自定義註解,並在其上添加了 @Documented,任何使用了該註解的地方,在生成 Javadoc 時都會包含該註解。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}

案例測試

// 1. 定義時使用Documented註解
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ApiDescription {
    String value();
}


// 2. 在類中使用自定義註解
@ApiDescription("註解描述。")
public class UserController {
}


// 3. 在shell中執行命令,生成的 HTML 文檔中將包含註解 @ApiDescription 的信息。
javadoc -d doc UserController.java

5 Inherited註解

元註解(meta-annotation),它控制一個註解是否會被子類繼承。

  • @Inherited 只能用在自定義註解的定義上;
  • 它表示:如果某個類使用了某個帶有@Inherited 的註解,那麼它的子類也會自動繼承該註解;
  • 僅對類(class)有效,對方法、字段、構造器等無效。

總結

使用範圍自定義註解一般結合 Spring Boot + AOP 實現:

  • 登錄校驗:@LoginRequired
  • 權限控制:@Permission("admin")
  • 接口限流:@RateLimit(limit = 5, seconds = 60)
  • 日誌埋點:@TrackEvent(name = "點擊提交")

上述這些也都是在Java語言中,使用Spring框架使用的常用方式。

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.