最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JAVA 注解Annotation開發(fā)流程從原理到實戰(zhàn)應(yīng)用

 更新時間:2026年04月10日 10:45:13   作者:小林說AI  
本文詳細介紹了Java注解(Annotation)的核心概念、分類及在開發(fā)中的應(yīng)用價值,重點講解了注解在AOP、ORM、權(quán)限校驗和單元測試等高級應(yīng)用場景中的使用方法,并提供了注解的最佳實踐建議和一個基于注解的權(quán)限校驗框架的實戰(zhàn)案例

1.1 本章學(xué)習(xí)目標(biāo)與重點

?? 掌握注解的核心概念與分類,理解注解在Java開發(fā)中的核心價值。
?? 熟練使用JDK內(nèi)置注解,掌握自定義注解的定義、解析與使用流程。
?? 掌握注解的元注解配置方式,理解不同元注解對自定義注解的約束作用。
?? 結(jié)合反射機制實現(xiàn)注解的實戰(zhàn)應(yīng)用,掌握注解在框架開發(fā)中的核心用法。
?? 本章重點是 自定義注解的開發(fā)流程注解與反射結(jié)合的實戰(zhàn)應(yīng)用,這是Java高級開發(fā)與框架設(shè)計的必備技能。

1.2 注解的核心概念與價值

1.2.1 什么是注解

?? 注解(Annotation) 是Java 5引入的一種特殊標(biāo)記,它可以在編譯期、類加載期、運行時被讀取,并執(zhí)行相應(yīng)的處理邏輯。注解本身不直接影響代碼的執(zhí)行邏輯,而是通過元數(shù)據(jù)的方式為程序提供額外信息,這些信息可以被編譯器、虛擬機或自定義的注解處理器解析和使用。

注解的本質(zhì)是一個繼承了 java.lang.annotation.Annotation 接口的特殊接口,我們定義的每一個注解,最終都會被編譯器生成對應(yīng)的接口實現(xiàn)類,供程序在運行時調(diào)用。

1.2.2 注解的核心價值

在傳統(tǒng)Java開發(fā)中,我們需要通過配置文件(如XML)來描述程序的元數(shù)據(jù)信息,而注解的出現(xiàn),讓元數(shù)據(jù)可以直接嵌入到代碼中,實現(xiàn)了代碼與配置的一體化。注解的核心價值主要體現(xiàn)在以下幾個方面:

  1. 簡化配置:替代XML等外部配置文件,減少配置文件的數(shù)量,降低維護成本。
  2. 增強代碼可讀性:元數(shù)據(jù)與代碼緊密結(jié)合,開發(fā)者可以直接在代碼中看到配置信息。
  3. 編譯期檢查:部分注解可以在編譯期進行語法檢查,提前發(fā)現(xiàn)潛在問題。
  4. 框架開發(fā)基石:是Spring、MyBatis等主流框架的核心技術(shù),用于實現(xiàn)依賴注入、AOP、ORM映射等功能。

1.2.3 注解的分類

根據(jù)注解的使用范圍和生命周期,我們可以將注解分為以下三類:

分類標(biāo)準具體類型特點典型示例
JDK內(nèi)置注解基本注解用于修飾代碼元素(類、方法、屬性等)@Override、@Deprecated、@SuppressWarnings
元注解注解的注解用于修飾自定義注解,約束注解的使用范圍和生命周期@Target、@Retention、@Documented、@Inherited
自定義注解業(yè)務(wù)注解開發(fā)者根據(jù)業(yè)務(wù)需求自行定義的注解,用于實現(xiàn)特定功能@RequestMapping、@Autowired、@Test

? 核心結(jié)論:注解是一種元數(shù)據(jù)標(biāo)記,本身不執(zhí)行任何邏輯,需要通過注解處理器(如反射、APT工具)解析后,才能發(fā)揮實際作用。

1.3 JDK內(nèi)置注解詳解

JDK提供了三個最基礎(chǔ)的內(nèi)置注解,用于輔助代碼開發(fā),這三個注解也是我們?nèi)粘i_發(fā)中使用頻率最高的注解。

1.3.1@Override:重寫檢查注解

?? 作用:用于標(biāo)記方法是重寫自父類或接口的方法,編譯器會檢查該方法的簽名是否與父類完全一致,如果不一致則編譯報錯。
?? 注意事項:該注解只能修飾方法,且只能用于重寫場景,不能用于重載。

代碼實操

/**
 * @Override 注解使用示例
 */
class ParentClass {
    public void sayHello() {
        System.out.println("父類的sayHello方法");
    }
}
class ChildClass extends ParentClass {
    // 使用@Override標(biāo)記該方法是重寫自父類
    @Override
    public void sayHello() {
        System.out.println("子類重寫的sayHello方法");
    }
    // 錯誤示例:方法簽名與父類不一致,編譯報錯
    // @Override
    // public void sayHello(String name) {
    //     System.out.println("Hello " + name);
    // }
}
public class OverrideDemo {
    public static void main(String[] args) {
        ParentClass child = new ChildClass();
        child.sayHello(); // 輸出:子類重寫的sayHello方法
    }
}

1.3.2@Deprecated:過時標(biāo)記注解

?? 作用:用于標(biāo)記類、方法、屬性等元素已過時,不推薦使用。編譯器會對使用該元素的代碼發(fā)出警告,提示開發(fā)者更換新的API。
?? 注意事項:該注解標(biāo)記的元素仍然可以使用,但存在被移除的風(fēng)險,建議開發(fā)者使用替代方案。

代碼實操

/**
 * @Deprecated 注解使用示例
 */
public class DeprecatedDemo {
    // 標(biāo)記該方法已過時
    @Deprecated
    public static void oldMethod() {
        System.out.println("這個方法已經(jīng)過時了,請使用newMethod替代");
    }
    // 新的替代方法
    public static void newMethod() {
        System.out.println("這是新的推薦方法");
    }
    public static void main(String[] args) {
        // 使用過時方法,編譯器會發(fā)出警告
        oldMethod();
        // 使用推薦方法
        newMethod();
    }
}

1.3.3@SuppressWarnings:抑制警告注解

?? 作用:用于抑制編譯器產(chǎn)生的特定類型警告,讓代碼編譯時不再輸出警告信息。
?? 注意事項:該注解需要傳入?yún)?shù),指定要抑制的警告類型,常用參數(shù)有 unchecked(抑制未檢查轉(zhuǎn)換警告)、deprecation(抑制過時元素警告)、all(抑制所有警告)。

代碼實操

import java.util.ArrayList;
import java.util.List;
/**
 * @SuppressWarnings 注解使用示例
 */
public class SuppressWarningsDemo {
    public static void main(String[] args) {
        // 抑制未檢查轉(zhuǎn)換警告
        @SuppressWarnings("unchecked")
        List<String> list = new ArrayList(); // 原始類型ArrayList,默認會產(chǎn)生警告
        list.add("Hello Annotation");
        System.out.println(list.get(0));
        // 抑制過時方法警告
        @SuppressWarnings("deprecation")
        void test() {
            DeprecatedDemo.oldMethod(); // 調(diào)用過時方法,不會產(chǎn)生警告
        }
        test();
    }
}

? 核心結(jié)論:JDK內(nèi)置注解主要用于代碼檢查和提示,幫助開發(fā)者寫出更規(guī)范的代碼,提升代碼質(zhì)量。

1.4 元注解詳解

元注解是用于修飾自定義注解的注解,JDK提供了四個標(biāo)準元注解,它們分別從不同維度約束自定義注解的行為。

1.4.1@Target:注解目標(biāo)約束

?? 作用:指定自定義注解可以修飾的代碼元素類型,如類、方法、屬性等。
?? 注意事項:如果不指定 @Target,則該注解可以修飾所有類型的代碼元素。

@Target 的參數(shù)是一個 ElementType 枚舉數(shù)組,常用枚舉值如下:

枚舉值可修飾的代碼元素
TYPE類、接口、枚舉
METHOD方法
FIELD屬性(成員變量)
PARAMETER方法參數(shù)
CONSTRUCTOR構(gòu)造方法
LOCAL_VARIABLE局部變量
ANNOTATION_TYPE注解

1.4.2@Retention:注解生命周期約束

?? 作用:指定注解的生命周期,即注解在哪個階段有效。
?? 注意事項:這是自定義注解必須指定的元注解,否則注解默認在編譯期就會被丟棄,無法在運行時通過反射獲取。

@Retention 的參數(shù)是一個 RetentionPolicy 枚舉值,三種枚舉值對應(yīng)注解的三個生命周期:

枚舉值生命周期特點適用場景
SOURCE源碼階段注解僅存在于源碼中,編譯成字節(jié)碼后被丟棄編譯器檢查,如 @Override
CLASS字節(jié)碼階段注解存在于源碼和字節(jié)碼中,類加載時被丟棄類加載器處理,如字節(jié)碼增強
RUNTIME運行時階段注解存在于源碼、字節(jié)碼和運行時,可通過反射獲取框架開發(fā),如Spring注解

1.4.3@Documented:文檔生成標(biāo)記

?? 作用:指定該注解會被 javadoc 工具提取到API文檔中。
?? 注意事項:默認情況下,注解不會出現(xiàn)在API文檔中,添加該元注解后,注解信息會被包含在文檔里。

1.4.4@Inherited:注解繼承標(biāo)記

?? 作用:指定該注解可以被子類繼承,如果父類使用了該注解,子類會自動繼承該注解。
?? 注意事項:該注解僅對類級別注解有效,對方法、屬性等注解無效。

代碼實操:元注解使用示例

import java.lang.annotation.*;
/**
 * 自定義注解 + 元注解使用示例
 * 該注解用于標(biāo)記類的作者信息
 */
// 該注解只能修飾類
@Target(ElementType.TYPE)
// 注解在運行時有效,可通過反射獲取
@Retention(RetentionPolicy.RUNTIME)
// 注解信息會被javadoc提取
@Documented
// 注解可以被子類繼承
@Inherited
public @interface Author {
    // 注解屬性:作者姓名,必填
    String name();
    // 注解屬性:作者郵箱,可選,默認值為空字符串
    String email() default "";
}

? 核心結(jié)論:元注解是自定義注解的規(guī)則約束器,通過元注解可以精準控制自定義注解的使用范圍和生命周期。

1.5 自定義注解的開發(fā)流程

自定義注解是注解開發(fā)的核心,掌握自定義注解的定義、使用和解析流程,是實現(xiàn)注解驅(qū)動開發(fā)的關(guān)鍵。

1.5.1 步驟1:定義自定義注解

定義自定義注解的語法格式與定義接口類似,只是在 interface 關(guān)鍵字前加了一個 @ 符號。注解中可以定義屬性,屬性的定義方式與接口方法類似,支持指定默認值。

語法格式

[元注解]
public @interface 注解名稱 {
    // 注解屬性定義
    屬性類型 屬性名() [default 默認值];
}

代碼實操:定義兩個常用自定義注解

import java.lang.annotation.*;
/**
 * 注解1:用于標(biāo)記業(yè)務(wù)日志,記錄方法的操作信息
 */
@Target(ElementType.METHOD) // 僅修飾方法
@Retention(RetentionPolicy.RUNTIME) // 運行時有效
public @interface Log {
    // 操作描述
    String desc();
    // 操作類型,默認值為"OTHER"
    String type() default "OTHER";
}
/**
 * 注解2:用于標(biāo)記數(shù)據(jù)校驗,校驗方法參數(shù)的合法性
 */
@Target(ElementType.PARAMETER) // 僅修飾方法參數(shù)
@Retention(RetentionPolicy.RUNTIME) // 運行時有效
public @interface Validate {
    // 參數(shù)不能為空
    boolean notNull() default true;
    // 參數(shù)長度最小值,默認值為0
    int minLength() default 0;
}

1.5.2 步驟2:使用自定義注解

定義好自定義注解后,就可以在對應(yīng)的代碼元素上使用該注解,使用時需要為注解的必填屬性賦值,可選屬性可以使用默認值。

代碼實操:使用自定義注解

/**
 * 自定義注解使用示例:用戶服務(wù)類
 */
public class UserService {
    // 使用@Log注解標(biāo)記方法,記錄操作日志
    @Log(desc = "根據(jù)ID查詢用戶", type = "QUERY")
    public String getUserById(
            // 使用@Validate注解標(biāo)記參數(shù),校驗參數(shù)合法性
            @Validate(notNull = true, minLength = 1) String userId) {
        if (userId == null || userId.length() < 1) {
            return "參數(shù)不合法";
        }
        return "用戶ID:" + userId + ",用戶名:張三";
    }
    @Log(desc = "添加用戶", type = "ADD")
    public String addUser(
            @Validate(notNull = true, minLength = 2) String username) {
        if (username == null || username.length() < 2) {
            return "用戶名長度不能小于2";
        }
        return "添加用戶成功:" + username;
    }
}

1.5.3 步驟3:解析自定義注解

注解本身不會執(zhí)行任何邏輯,必須通過注解解析器來讀取注解信息,并執(zhí)行相應(yīng)的處理邏輯。在Java中,最常用的注解解析方式是反射機制

解析流程

① 獲取目標(biāo)類的 Class 對象。
② 獲取目標(biāo)方法或參數(shù)的注解對象。
③ 讀取注解的屬性值,執(zhí)行自定義業(yè)務(wù)邏輯。

代碼實操:反射解析自定義注解

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
/**
 * 注解解析器:解析@Log和@Validate注解
 */
public class AnnotationParser {
    /**
     * 解析@Log注解,輸出方法操作日志
     */
    public static void parseLogAnnotation(Class<?> clazz) {
        // 獲取類中所有方法
        Method[] methods = clazz.getDeclaredMethods();
        for (Method method : methods) {
            // 判斷方法是否有@Log注解
            if (method.isAnnotationPresent(Log.class)) {
                Log log = method.getAnnotation(Log.class);
                System.out.println("===== 方法操作日志 =====");
                System.out.println("方法名:" + method.getName());
                System.out.println("操作描述:" + log.desc());
                System.out.println("操作類型:" + log.type());
                System.out.println("-----------------------");
            }
        }
    }
    /**
     * 解析@Validate注解,校驗方法參數(shù)合法性
     */
    public static Object invokeAndValidate(Object obj, String methodName, Object... args) throws Exception {
        // 獲取目標(biāo)方法
        Method method = obj.getClass().getDeclaredMethod(methodName, getParameterTypes(args));
        Parameter[] parameters = method.getParameters();
        // 校驗參數(shù)
        for (int i = 0; i < parameters.length; i++) {
            Parameter param = parameters[i];
            if (param.isAnnotationPresent(Validate.class)) {
                Validate validate = param.getAnnotation(Validate.class);
                Object arg = args[i];
                String paramName = param.getName();
                // 非空校驗
                if (validate.notNull() && arg == null) {
                    throw new IllegalArgumentException(paramName + " 不能為null");
                }
                // 長度校驗
                if (arg instanceof String && ((String) arg).length() < validate.minLength()) {
                    throw new IllegalArgumentException(paramName + " 長度不能小于" + validate.minLength());
                }
            }
        }
        // 調(diào)用目標(biāo)方法
        return method.invoke(obj, args);
    }
    // 獲取參數(shù)類型數(shù)組
    private static Class<?>[] getParameterTypes(Object... args) {
        if (args == null || args.length == 0) {
            return new Class[0];
        }
        Class<?>[] types = new Class[args.length];
        for (int i = 0; i < args.length; i++) {
            types[i] = args[i].getClass();
        }
        return types;
    }
    // 測試方法
    public static void main(String[] args) throws Exception {
        UserService userService = new UserService();
        // 解析@Log注解
        parseLogAnnotation(UserService.class);
        // 解析@Validate注解并調(diào)用方法
        System.out.println("正常調(diào)用:" + invokeAndValidate(userService, "getUserById", "1001"));
        System.out.println("參數(shù)非法調(diào)用:" + invokeAndValidate(userService, "getUserById", ""));
    }
}

輸出結(jié)果

===== 方法操作日志 =====
方法名:getUserById
操作描述:根據(jù)ID查詢用戶
操作類型:QUERY
-----------------------
===== 方法操作日志 =====
方法名:addUser
操作描述:添加用戶
操作類型:ADD
-----------------------
正常調(diào)用:用戶ID:1001,用戶名:張三
Exception in thread "main" java.lang.IllegalArgumentException: arg0 長度不能小于1

? 核心結(jié)論:自定義注解的開發(fā)流程是 定義 → 使用 → 解析,其中解析是核心步驟,反射是最常用的解析方式。

1.6 注解的高級應(yīng)用場景

注解在Java開發(fā)中應(yīng)用廣泛,尤其是在框架開發(fā)和業(yè)務(wù)系統(tǒng)設(shè)計中,以下是幾個典型的高級應(yīng)用場景。

1.6.1 場景1:基于注解的AOP實現(xiàn)

?? 核心思想:通過注解標(biāo)記需要增強的方法,結(jié)合動態(tài)代理實現(xiàn)AOP(面向切面編程),在方法執(zhí)行前后添加通用邏輯(如日志、事務(wù)、權(quán)限校驗)。

代碼實操:注解+動態(tài)代理實現(xiàn)日志切面

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
 * 基于注解的AOP日志切面
 */
// 定義切面注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface LogAspect {
}
// 業(yè)務(wù)接口
interface OrderService {
    @LogAspect
    void createOrder(String orderId);
}
// 業(yè)務(wù)實現(xiàn)類
class OrderServiceImpl implements OrderService {
    @Override
    public void createOrder(String orderId) {
        System.out.println("創(chuàng)建訂單:" + orderId);
    }
}
// 日志切面處理器
class LogAspectHandler implements InvocationHandler {
    private Object target;
    public LogAspectHandler(Object target) {
        this.target = target;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 方法執(zhí)行前:判斷是否有@LogAspect注解
        if (method.isAnnotationPresent(LogAspect.class)) {
            System.out.println("[前置日志] 方法 " + method.getName() + " 開始執(zhí)行,參數(shù):" + args[0]);
        }
        // 執(zhí)行目標(biāo)方法
        Object result = method.invoke(target, args);
        // 方法執(zhí)行后
        if (method.isAnnotationPresent(LogAspect.class)) {
            System.out.println("[后置日志] 方法 " + method.getName() + " 執(zhí)行完畢");
        }
        return result;
    }
    // 創(chuàng)建代理對象
    public static <T> T createProxy(T target) {
        return (T) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new LogAspectHandler(target)
        );
    }
}
// 測試類
public class AnnotationAopDemo {
    public static void main(String[] args) {
        OrderService orderService = new OrderServiceImpl();
        OrderService proxy = LogAspectHandler.createProxy(orderService);
        proxy.createOrder("ORDER_20260129");
    }
}

輸出結(jié)果

[前置日志] 方法 createOrder 開始執(zhí)行,參數(shù):ORDER_20260129
創(chuàng)建訂單:ORDER_20260129
[后置日志] 方法 createOrder 執(zhí)行完畢

1.6.2 場景2:基于注解的ORM映射

?? 核心思想:通過注解標(biāo)記實體類與數(shù)據(jù)庫表的映射關(guān)系,結(jié)合反射實現(xiàn)自動生成SQL語句,簡化數(shù)據(jù)庫操作。

代碼實操:注解實現(xiàn)ORM映射

import java.lang.annotation.*;
/**
 * 基于注解的ORM映射示例
 */
// 表映射注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Table {
    String name();
}
// 字段映射注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Column {
    String name();
    String type() default "VARCHAR";
    int length() default 255;
}
// 實體類
@Table(name = "t_user")
class User {
    @Column(name = "user_id", type = "INT", length = 10)
    private Integer userId;
    @Column(name = "user_name", type = "VARCHAR", length = 50)
    private String userName;
    @Column(name = "age", type = "INT", length = 3)
    private Integer age;
    // 省略getter/setter方法
    public Integer getUserId() { return userId; }
    public void setUserId(Integer userId) { this.userId = userId; }
    public String getUserName() { return userName; }
    public void setUserName(String userName) { this.userName = userName; }
    public Integer getAge() { return age; }
    public void setAge(Integer age) { this.age = age; }
}
// ORM工具類:生成SQL語句
class OrmUtil {
    public static String generateSelectSql(Class<?> clazz) {
        // 獲取表名
        Table table = clazz.getAnnotation(Table.class);
        if (table == null) {
            throw new IllegalArgumentException("實體類未添加@Table注解");
        }
        String tableName = table.name();
        // 獲取字段名
        StringBuilder columns = new StringBuilder();
        java.lang.reflect.Field[] fields = clazz.getDeclaredFields();
        for (java.lang.reflect.Field field : fields) {
            Column column = field.getAnnotation(Column.class);
            if (column != null) {
                columns.append(column.name()).append(",");
            }
        }
        // 移除最后一個逗號
        columns.deleteCharAt(columns.length() - 1);
        // 生成查詢SQL
        return "SELECT " + columns + " FROM " + tableName;
    }
    public static void main(String[] args) {
        System.out.println("生成的查詢SQL:" + generateSelectSql(User.class));
    }
}

輸出結(jié)果

生成的查詢SQL:SELECT user_id,user_name,age FROM t_user

1.6.3 場景3:基于注解的單元測試

?? 核心思想:通過注解標(biāo)記測試方法,測試框架自動識別并執(zhí)行這些方法,實現(xiàn)自動化測試。這正是JUnit框架的核心實現(xiàn)原理。

代碼實操:簡化版JUnit框架

import java.lang.annotation.*;
import java.lang.reflect.Method;
/**
 * 簡化版JUnit框架:基于注解的單元測試
 */
// 測試方法注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface TestCase {
}
// 測試類
class MathTest {
    @TestCase
    public void testAdd() {
        int result = 1 + 2;
        assert result == 3 : "加法測試失敗";
        System.out.println("testAdd 測試通過");
    }
    @TestCase
    public void testMultiply() {
        int result = 2 * 3;
        assert result == 6 : "乘法測試失敗";
        System.out.println("testMultiply 測試通過");
    }
    // 非測試方法,不會被執(zhí)行
    public void normalMethod() {
        System.out.println("這是普通方法,不會被測試");
    }
}
// 測試運行器
class TestRunner {
    public static void run(Class<?> testClass) throws Exception {
        Object obj = testClass.getDeclaredConstructor().newInstance();
        Method[] methods = testClass.getDeclaredMethods();
        int passCount = 0;
        int failCount = 0;
        for (Method method : methods) {
            if (method.isAnnotationPresent(TestCase.class)) {
                try {
                    method.invoke(obj);
                    passCount++;
                } catch (Exception e) {
                    failCount++;
                    System.out.println(method.getName() + " 測試失?。? + e.getCause().getMessage());
                }
            }
        }
        System.out.println("\n===== 測試結(jié)果 ======");
        System.out.println("總測試數(shù):" + (passCount + failCount));
        System.out.println("通過數(shù):" + passCount);
        System.out.println("失敗數(shù):" + failCount);
    }
    public static void main(String[] args) throws Exception {
        TestRunner.run(MathTest.class);
    }
}

輸出結(jié)果

testAdd 測試通過
testMultiply 測試通過

===== 測試結(jié)果 ======
總測試數(shù):2
通過數(shù):2
失敗數(shù):0

1.7 注解的優(yōu)缺點與最佳實踐

1.7.1 注解的優(yōu)點

  1. 簡化開發(fā):減少冗余配置,提高開發(fā)效率。
  2. 增強代碼可讀性:元數(shù)據(jù)與代碼緊密結(jié)合,一目了然。
  3. 提高代碼靈活性:通過注解可以動態(tài)控制程序行為,無需修改核心代碼。
  4. 框架友好:是現(xiàn)代Java框架的標(biāo)配,便于集成主流框架。

1.7.2 注解的缺點

  1. 侵入性強:注解直接嵌入代碼,修改注解需要修改源碼。
  2. 可讀性差:過多的注解會導(dǎo)致代碼臃腫,影響閱讀。
  3. 性能損耗:運行時注解需要通過反射解析,存在一定的性能開銷。
  4. 調(diào)試困難:注解的邏輯在運行時執(zhí)行,調(diào)試時難以跟蹤。

1.7.3 注解的最佳實踐

  1. 合理選擇注解生命周期:僅在需要反射解析時使用 RUNTIME,否則使用 SOURCECLASS,減少性能損耗。
  2. 避免過度使用注解:不要用注解替代所有配置,對于經(jīng)常變化的配置,建議使用外部配置文件。
  3. 做好注解文檔:為自定義注解添加詳細的JavaDoc注釋,說明注解的作用和使用方式。
  4. 結(jié)合設(shè)計模式使用:如結(jié)合工廠模式、代理模式,實現(xiàn)注解的靈活解析和擴展。
  5. 優(yōu)先使用成熟框架的注解:如Spring的 @Autowired、@RequestMapping,避免重復(fù)造輪子。

1.8 實戰(zhàn)案例:基于注解的權(quán)限校驗框架

1.8.1 需求分析

?? 實現(xiàn)一個輕量級的權(quán)限校驗框架,要求:

  1. 通過注解標(biāo)記需要權(quán)限校驗的方法。
  2. 支持指定方法所需的權(quán)限角色。
  3. 在方法執(zhí)行前自動校驗用戶權(quán)限,無權(quán)限則拋出異常。
  4. 框架具備良好的擴展性,支持自定義權(quán)限校驗邏輯。

1.8.2 代碼實現(xiàn)

import java.lang.annotation.*;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
/**
 * 基于注解的權(quán)限校驗框架
 */
// 權(quán)限校驗注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequiresPermission {
    // 所需權(quán)限角色
    String[] value();
}
// 用戶上下文類:存儲當(dāng)前登錄用戶信息
class UserContext {
    private static ThreadLocal<String> currentUser = new ThreadLocal<>();
    private static Set<String> userRoles = new HashSet<>();
    // 設(shè)置當(dāng)前用戶
    public static void setCurrentUser(String username, String... roles) {
        currentUser.set(username);
        userRoles.clear();
        userRoles.addAll(Arrays.asList(roles));
    }
    // 獲取當(dāng)前用戶角色
    public static Set<String> getUserRoles() {
        return userRoles;
    }
    // 清除當(dāng)前用戶
    public static void clear() {
        currentUser.remove();
        userRoles.clear();
    }
}
// 權(quán)限校驗切面類
class PermissionAspect {
    /**
     * 執(zhí)行方法并校驗權(quán)限
     */
    public static Object invoke(Object obj, String methodName, Object... args) throws Exception {
        Method method = obj.getClass().getDeclaredMethod(methodName, getParameterTypes(args));
        // 權(quán)限校驗
        if (method.isAnnotationPresent(RequiresPermission.class)) {
            RequiresPermission annotation = method.getAnnotation(RequiresPermission.class);
            String[] requiredRoles = annotation.value();
            Set<String> userRoles = UserContext.getUserRoles();
            // 檢查是否有匹配的角色
            boolean hasPermission = false;
            for (String role : requiredRoles) {
                if (userRoles.contains(role)) {
                    hasPermission = true;
                    break;
                }
            }
            if (!hasPermission) {
                throw new SecurityException("無權(quán)限訪問該方法,所需角色:" + Arrays.toString(requiredRoles));
            }
        }
        // 執(zhí)行目標(biāo)方法
        return method.invoke(obj, args);
    }
    private static Class<?>[] getParameterTypes(Object... args) {
        if (args == null || args.length == 0) {
            return new Class[0];
        }
        Class<?>[] types = new Class[args.length];
        for (int i = 0; i < args.length; i++) {
            types[i] = args[i].getClass();
        }
        return types;
    }
}
// 業(yè)務(wù)服務(wù)類
class AdminService {
    @RequiresPermission({"ADMIN", "SUPER_ADMIN"})
    public String deleteUser(String userId) {
        return "刪除用戶成功:" + userId;
    }
    @RequiresPermission({"USER", "ADMIN", "SUPER_ADMIN"})
    public String queryUser(String userId) {
        return "查詢用戶信息:" + userId;
    }
}
// 測試類
public class PermissionFrameworkDemo {
    public static void main(String[] args) throws Exception {
        AdminService adminService = new AdminService();
        // 測試1:管理員用戶,擁有ADMIN角色
        UserContext.setCurrentUser("admin", "ADMIN");
        System.out.println(PermissionAspect.invoke(adminService, "deleteUser", "1001"));
        System.out.println(PermissionAspect.invoke(adminService, "queryUser", "1001"));
        // 測試2:普通用戶,只有USER角色
        UserContext.setCurrentUser("user", "USER");
        System.out.println(PermissionAspect.invoke(adminService, "queryUser", "1001"));
        try {
            PermissionAspect.invoke(adminService, "deleteUser", "1001");
        } catch (Exception e) {
            System.out.println(e.getCause().getMessage());
        }
        UserContext.clear();
    }
}

輸出結(jié)果

刪除用戶成功:1001
查詢用戶信息:1001
查詢用戶信息:1001
無權(quán)限訪問該方法,所需角色:[ADMIN, SUPER_ADMIN]

1.8.3 案例總結(jié)

? 這個權(quán)限校驗框架綜合運用了注解定義、反射解析、線程本地存儲等技術(shù),核心亮點如下:

  1. 使用 @RequiresPermission 注解標(biāo)記需要權(quán)限校驗的方法,配置簡單。
  2. 通過 UserContext 存儲當(dāng)前用戶角色,支持多線程環(huán)境。
  3. 利用反射解析注解,在方法執(zhí)行前自動校驗權(quán)限,無侵入性。
  4. 框架擴展性強,可通過修改 PermissionAspect 類實現(xiàn)自定義權(quán)限校驗邏輯。

1.9 本章總結(jié)

  1. 注解是Java的一種元數(shù)據(jù)標(biāo)記,本身不執(zhí)行邏輯,需要通過解析器(反射、APT)處理后才能發(fā)揮作用。
  2. JDK內(nèi)置注解用于代碼檢查,元注解用于約束自定義注解,自定義注解是業(yè)務(wù)開發(fā)的核心。
  3. 自定義注解的開發(fā)流程是 定義 → 使用 → 解析,反射是最常用的運行時解析方式。
  4. 注解是框架開發(fā)的基石,廣泛應(yīng)用于AOP、ORM、權(quán)限校驗、單元測試等場景。
  5. 注解的優(yōu)點是簡化配置、增強可讀性,缺點是侵入性強、有性能損耗,使用時需平衡利弊。
  6. 注解的最佳實踐是“合理選擇生命周期、避免過度使用、做好文檔注釋”,結(jié)合設(shè)計模式提升擴展性。

到此這篇關(guān)于JAVA 注解Annotation開發(fā)流程從原理到實戰(zhàn)應(yīng)用的文章就介紹到這了,更多相關(guān)java注解annotation內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • MyEclipse到期破解代碼分享

    MyEclipse到期破解代碼分享

    前幾天有個小伙伴咨詢,使用的時候一直說myeclipse已過期,需要購買,如何解決?可以去網(wǎng)上搜搜注冊碼,但作為程序猿這么做簡直太無趣,看看我們自己來解決這個問題
    2014-11-11
  • java DataInputStream和DataOutputStream詳解及實例代碼

    java DataInputStream和DataOutputStream詳解及實例代碼

    這篇文章主要介紹了java DataInputStream和DataOutputStream詳解及實例代碼的相關(guān)資料,需要的朋友可以參考下
    2017-01-01
  • Java NIO服務(wù)器端開發(fā)詳解

    Java NIO服務(wù)器端開發(fā)詳解

    這篇文章主要介紹了Java NIO服務(wù)器端開發(fā)詳解,具有一定借鑒價值,需要的朋友可以參考下。
    2017-12-12
  • Java設(shè)計模式之責(zé)任鏈模式詳解

    Java設(shè)計模式之責(zé)任鏈模式詳解

    客戶端發(fā)出一個請求,鏈上的對象都有機會來處理這一請求,而客戶端不需要知道誰是具體的處理對象。這樣就實現(xiàn)了請求者和接受者之間的解耦,并且在客戶端可以實現(xiàn)動態(tài)的組合職責(zé)鏈。使編程更有靈活性
    2022-07-07
  • SpringBoot2.x 整合 AntiSamy防御XSS攻擊的簡單總結(jié)

    SpringBoot2.x 整合 AntiSamy防御XSS攻擊的簡單總結(jié)

    本文主要對SpringBoot2.x集成AntiSamy防御XSS攻擊進行簡單總結(jié),其中SpringBoot使用的2.4.5版本,通過示例代碼給大家介紹的非常詳細,需要的朋友參考下吧
    2021-08-08
  • Kafka中的producer攔截器與consumer攔截器詳解

    Kafka中的producer攔截器與consumer攔截器詳解

    這篇文章主要介紹了Kafka中的producer攔截器與consumer攔截器詳解,Producer 的Interceptor使得用戶在消息發(fā)送前以及Producer回調(diào)邏輯前有機會對消息做 一些定制化需求,比如修改消息等,需要的朋友可以參考下
    2023-12-12
  • SpringBoot3應(yīng)用中集成和使用Spring Retry的實踐記錄

    SpringBoot3應(yīng)用中集成和使用Spring Retry的實踐記錄

    SpringRetry為SpringBoot3提供重試機制,支持注解和編程式兩種方式,可配置重試策略與監(jiān)聽器,適用于臨時性故障場景,需合理設(shè)置次數(shù)、退避策略并做好監(jiān)控,本文給大家詳細介紹如何在 SpringBoot 3 應(yīng)用中集成和使用 Spring Retry,感興趣的朋友一起看看吧
    2025-06-06
  • 獲取Spring的上下文環(huán)境ApplicationContext的最簡單方式

    獲取Spring的上下文環(huán)境ApplicationContext的最簡單方式

    這篇文章主要介紹了獲取Spring的上下文環(huán)境ApplicationContext的最簡單方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • IDEA中使用Gradle構(gòu)建項目中文報GBK錯誤的解決方案

    IDEA中使用Gradle構(gòu)建項目中文報GBK錯誤的解決方案

    這篇文章主要介紹了IDEA中使用Gradle構(gòu)建項目中文報GBK錯誤的解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2025-04-04
  • 理解Spring中的依賴注入和控制反轉(zhuǎn)

    理解Spring中的依賴注入和控制反轉(zhuǎn)

    這篇文章主要介紹了理解Spring中的依賴注入和控制反轉(zhuǎn),具有一定參考價值,需要的朋友可以了解下。
    2017-11-11

最新評論

化隆| 宁乡县| 青阳县| 化德县| 海阳市| 曲水县| 溧水县| 新建县| 德惠市| 金山区| 海兴县| 墨脱县| 榆中县| 印江| 宣武区| 武邑县| 五家渠市| 宣城市| 科技| 东兰县| 石台县| 大荔县| 渝中区| 樟树市| 遂宁市| 沙河市| 梨树县| 渑池县| 灵台县| 扎赉特旗| 屯昌县| 集安市| 绍兴县| 石阡县| 巫溪县| 永德县| 晋中市| 英超| 绥滨县| 大田县| 息烽县|