欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java 注解與反射實戰(zhàn)之自定義注解從入門到精通

 更新時間:2025年10月22日 09:37:28   作者:梵得兒SHI  
本文給大家介紹Java 注解與反射實戰(zhàn)之自定義注解從入門到精通,本文將從基礎到實戰(zhàn),帶你掌握自定義注解的定義、元注解的作用,以及如何通過反射讓注解 "生效",感興趣的朋友跟隨小編一起看看吧

前言:注解到底是什么?

        你是否經(jīng)常在 Java 代碼中看到@Override、@Deprecated這樣的標記?這些就是注解 —— 一種給代碼 "貼標簽" 的機制。注解本身不直接影響代碼執(zhí)行,但能通過工具(如編譯器)或框架(如 Spring)賦予代碼額外含義。

        自定義注解則是讓我們根據(jù)業(yè)務需求創(chuàng)建專屬 "標簽",結合反射機制能實現(xiàn)強大的動態(tài)邏輯(比如日志記錄、權限校驗、ORM 映射等)。本文將從基礎到實戰(zhàn),帶你掌握自定義注解的定義、元注解的作用,以及如何通過反射讓注解 "生效"。

一、自定義注解基礎:@interface關鍵字

        自定義注解使用 @interface 關鍵字定義,本質上是一種特殊的接口(編譯后會生成繼承 java.lang.annotation.Annotation 的接口)。

1.1 最簡單的自定義注解

// 定義一個空注解
public @interface MyFirstAnnotation {
}

這個注解沒有任何屬性,僅作為標記使用??梢灾苯訕俗⒃陬悺⒎椒ǖ仍厣希?/p>

@MyFirstAnnotation
public class Demo {
    @MyFirstAnnotation
    public void test() {}
}

1.2 帶屬性的注解

注解可以包含 "屬性"(類似接口的抽象方法),使用時需要為屬性賦值(除非有默認值)。

public @interface UserInfo {
    // 字符串屬性
    String name();
    // 整數(shù)屬性,帶默認值
    int age() default 18;
    // 數(shù)組屬性
    String[] hobbies() default {"coding"};
}

使用時的語法(屬性名 = 值):

@UserInfo(name = "張三", age = 20, hobbies = {"籃球", "游戲"})
public class Person {}

?? 特殊規(guī)則:

  • 若屬性名是 value,且只有這一個屬性需要賦值,可省略屬性名:@MyAnnotation("test")
  • 數(shù)組屬性若只有一個元素,可省略大括號:hobbies = "足球"

二、元注解:注解的 "注解"

        元注解是用于修飾注解的注解,規(guī)定了自定義注解的使用范圍、生命周期等特性。Java 內置了 4 種元注解:@Target、@Retention、@Documented、@Inherited。

2.1@Target:指定注解能修飾哪些元素

   @Target 限制注解可標注的目標(如類、方法、字段等),參數(shù)是 ElementType 枚舉數(shù)組,常用值:

ElementType作用范圍
TYPE類、接口、枚舉
METHOD方法
FIELD成員變量(包括枚舉常量)
PARAMETER方法參數(shù)
CONSTRUCTOR構造方法
LOCAL_VARIABLE局部變量

示例:限制注解只能用于類和方法

import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
@Target({ElementType.TYPE, ElementType.METHOD}) // 可修飾類和方法
public @interface Log {
}

如果把 @Log 標注在字段上,編譯器會直接報錯:

public class Demo {
    @Log // 編譯錯誤:@Log不適用于字段
    private String name;
}

?? 圖示:@Target 的作用范圍限制

2.2@Retention:指定注解的生命周期

@Retention 決定注解保留到哪個階段(源碼、字節(jié)碼、運行時),參數(shù)是 RetentionPolicy 枚舉,必須指定:

RetentionPolicy生命周期說明能否被反射獲取
SOURCE僅存在于源碼中,編譯后丟棄(如@Override不能
CLASS保留到字節(jié)碼中,但 JVM 運行時不加載(默認值)不能
RUNTIME保留到運行時,JVM 加載,可通過反射獲取

示例:讓注解在運行時可被反射獲取

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME) // 關鍵:保留到運行時
public @interface Permission {
    String value();
}

?? 為什么 RUNTIME 重要?反射是在程序運行時動態(tài)獲取類信息的機制,只有 RUNTIME 級別的注解才能被反射讀取,這是注解與反射結合的核心前提。

?? 圖示:注解的生命周期流程

2.3@Documented:讓注解出現(xiàn)在 API 文檔中

        默認情況下,javadoc 生成的文檔不會包含注解信息。@Documented 修飾的注解會被包含在文檔中。

示例

import java.lang.annotation.Documented;
@Documented // 生成文檔時包含該注解
public @interface Description {
    String value();
}
/**
 * 測試類
 * @Description 這是一個測試類
 */
@Description("測試類")
public class Test {}

生成的 javadoc 中,Test 類的文檔會顯示 @Description("測試類")。

2.4@Inherited:讓注解可被繼承

@Inherited 表示注解具有繼承性:如果父類被該注解標注,子類會自動繼承該注解(僅對類注解有效,方法 / 字段注解不繼承)。

示例

import java.lang.annotation.Inherited;
@Inherited // 允許繼承
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface InheritedAnnotation {}
// 父類標注注解
@InheritedAnnotation
class Parent {}
// 子類未標注,但會繼承父類的@InheritedAnnotation
class Child extends Parent {}

通過反射驗證:

public class Test {
    public static void main(String[] args) {
        System.out.println(Child.class.isAnnotationPresent(InheritedAnnotation.class)); // 輸出:true
    }
}

?? 圖示:@Inherited 的繼承效果

三、注解 + 反射:讓注解 "生效"

        注解本身只是標記,必須通過反射獲取注解信息并執(zhí)行邏輯,才能真正發(fā)揮作用。反射提供了以下核心方法(在 ClassMethod、Field 等類中):

方法作用
getAnnotation(Class)獲取指定類型的注解實例
getAnnotations()獲取所有注解(包括繼承的)
isAnnotationPresent(Class)判斷是否存在指定注解

實戰(zhàn)案例:用注解實現(xiàn)方法權限校驗

需求:定義 @RequiresPermission 注解,標記方法需要的權限;通過反射調用方法前檢查當前用戶是否有權限,無權限則拋出異常。

步驟 1:定義注解

import java.lang.annotation.*;
@Target(ElementType.METHOD) // 僅用于方法
@Retention(RetentionPolicy.RUNTIME) // 運行時可反射獲取
public @interface RequiresPermission {
    String[] value(); // 所需權限列表
}

步驟 2:使用注解標注方法

public class UserService {
    // 需要"user:query"權限
    @RequiresPermission("user:query")
    public void queryUser() {
        System.out.println("查詢用戶成功");
    }
    // 需要"user:add"或"admin"權限
    @RequiresPermission({"user:add", "admin"})
    public void addUser() {
        System.out.println("新增用戶成功");
    }
}

步驟 3:反射 + 注解實現(xiàn)權限校驗

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public class PermissionChecker {
    // 模擬當前用戶擁有的權限
    private static final Set<String> CURRENT_USER_PERMISSIONS = new HashSet<>(Arrays.asList("user:query"));
    // 反射調用方法并校驗權限
    public static void invokeWithCheck(Object obj, String methodName) throws Exception {
        // 1. 獲取方法對象
        Method method = obj.getClass().getMethod(methodName);
        // 2. 檢查方法是否有@RequiresPermission注解
        if (method.isAnnotationPresent(RequiresPermission.class)) {
            // 3. 獲取注解實例
            RequiresPermission annotation = method.getAnnotation(RequiresPermission.class);
            // 4. 獲取注解的權限列表
            String[] requiredPermissions = annotation.value();
            // 5. 校驗權限
            boolean hasPermission = false;
            for (String permission : requiredPermissions) {
                if (CURRENT_USER_PERMISSIONS.contains(permission)) {
                    hasPermission = true;
                    break;
                }
            }
            if (!hasPermission) {
                throw new SecurityException("權限不足,需要:" + Arrays.toString(requiredPermissions));
            }
        }
        // 6. 權限通過,調用方法
        method.invoke(obj);
    }
    public static void main(String[] args) throws Exception {
        UserService service = new UserService();
        invokeWithCheck(service, "queryUser"); // 成功:查詢用戶成功
        invokeWithCheck(service, "addUser");  // 失敗:拋出SecurityException
    }
}

執(zhí)行結果:

查詢用戶成功
Exception in thread "main" java.lang.SecurityException: 權限不足,需要:[user:add, admin]

四、底層原理:注解本質與反射獲取機制

注解的本質@interface 編譯后會生成一個繼承 java.lang.annotation.Annotation 的接口,例如:

// 編譯后自動生成的代碼(簡化)
public interface MyAnnotation extends Annotation {
    String value();
    int age() default 18;
}
  • 注解實例的生成:當 JVM 加載被注解的類時,會通過動態(tài)代理生成注解接口的實現(xiàn)類實例(保存注解屬性值)。
  • 反射獲取注解的過程:反射通過 getAnnotation() 方法從類 / 方法的元數(shù)據(jù)中獲取代理實例,從而讀取屬性值。

五、應用場景總結

注解 + 反射的組合在框架中被廣泛使用:

  • 日志記錄:通過注解標記需要記錄日志的方法,反射攔截并打印日志(如 Spring 的@Log)。
  • ORM 映射:用注解關聯(lián) Java 類與數(shù)據(jù)庫表(如 JPA 的@Entity、@Column)。
  • 依賴注入:標記需要注入的對象(如 Spring 的@Autowired)。
  • AOP 切面:通過注解定義切入點(如 Spring 的@Before、@After)。
  • 參數(shù)校驗:驗證方法參數(shù)合法性(如 Jakarta 的@NotNull@Size)。

結語

        自定義注解是 Java 中 "聲明式編程" 的核心體現(xiàn),結合反射能極大簡化代碼邏輯、提高靈活性。掌握元注解的作用(尤其是@Target@Retention)是定義有效注解的前提,而反射則是讓注解從 "標記" 變?yōu)?"可執(zhí)行邏輯" 的橋梁。

嘗試在項目中用注解解決重復邏輯(如日志、權限),你會感受到它的強大!

到此這篇關于Java 注解與反射實戰(zhàn)之自定義注解從入門到精通的文章就介紹到這了,更多相關Java 注解與反射內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • springmvc下實現(xiàn)登錄驗證碼功能示例

    springmvc下實現(xiàn)登錄驗證碼功能示例

    本篇文章主要介紹了springmvc下實現(xiàn)登錄驗證碼功能示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-02-02
  • java compareTo和compare方法比較詳解

    java compareTo和compare方法比較詳解

    這篇文章主要介紹了java compareTo和compare方法比較詳解,本篇文章通過簡要的案例,講解了該項技術的了解與使用,以下就是詳細內容,需要的朋友可以參考下
    2021-09-09
  • prometheus數(shù)據(jù)遠程寫入elasticsearch方式

    prometheus數(shù)據(jù)遠程寫入elasticsearch方式

    這篇文章主要介紹了prometheus數(shù)據(jù)遠程寫入elasticsearch方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2025-06-06
  • Java實現(xiàn)CORS跨域請求的實現(xiàn)方法

    Java實現(xiàn)CORS跨域請求的實現(xiàn)方法

    本篇文章主要介紹了Java實現(xiàn)CORS跨域請求的實現(xiàn)方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-09-09
  • Springboot讀取配置文件及自定義配置文件的方法

    Springboot讀取配置文件及自定義配置文件的方法

    這篇文章主要介紹了Springboot讀取配置文件及自定義配置文件的方法,非常不錯,具有參考借鑒價值,需要的朋友可以參考下
    2017-12-12
  • Java數(shù)組轉換為List的四種方式

    Java數(shù)組轉換為List的四種方式

    這篇文章主要介紹了Java開發(fā)技巧數(shù)組轉List的四種方式總結,每種方式結合實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-09-09
  • 使用arthas命令redefine實現(xiàn)Java熱更新(推薦)

    使用arthas命令redefine實現(xiàn)Java熱更新(推薦)

    今天分享一個非常重要的命令 redefine ,主要作用是加載外部的 .class 文件,用來替換 JVM 已經(jīng)加載的類,總結起來就是實現(xiàn)了 Java 的熱更新,感興趣的朋友跟隨小編一起看看吧
    2020-05-05
  • Java如何通過反射將map轉換為實體對象

    Java如何通過反射將map轉換為實體對象

    在Java開發(fā)中,常需要將XML配置數(shù)據(jù)轉為Map,并最終映射到實體對象上,通過單例模式管理XML轉換后的Map,并利用Java反射機制,通過屬性名稱匹配將Map的值賦給實體對象的對應屬性,這種方法忽略了數(shù)據(jù)類型轉換,適用于數(shù)據(jù)類型一致的簡單場景,需要類型轉換時
    2024-09-09
  • 基于Spark實現(xiàn)隨機森林代碼

    基于Spark實現(xiàn)隨機森林代碼

    這篇文章主要為大家詳細介紹了基于Spark實現(xiàn)隨機森林代碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-08-08
  • 升級springboot中spring框架的版本的實現(xiàn)方法

    升級springboot中spring框架的版本的實現(xiàn)方法

    本文主要介紹了升級springboot中spring框架的版本的實現(xiàn)方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2024-08-08

最新評論