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

JAVA自定義注解的步驟示例詳解

 更新時(shí)間:2025年11月17日 15:30:55   作者:編程小Y  
本文介紹了Java中注解的基本概念、自定義注解的步驟、常用元注解的解析以及自定義注解的常見應(yīng)用場景,自定義注解可以幫助開發(fā)者實(shí)現(xiàn)日志記錄、權(quán)限校驗(yàn)、參數(shù)校驗(yàn)等通用功能,提高代碼的復(fù)用性和可讀性,感興趣的朋友跟隨小編一起看看吧

簡介

在 Java 中,注解(Annotation) 是一種特殊的元數(shù)據(jù)(Metadata),可以標(biāo)記在類、方法、字段、參數(shù)等元素上,用于傳遞額外信息。自定義注解允許開發(fā)者根據(jù)業(yè)務(wù)需求定義自己的注解,結(jié)合反射、AOP 等技術(shù)實(shí)現(xiàn)靈活的功能(如日志記錄、權(quán)限校驗(yàn)、參數(shù)校驗(yàn)等)。

一、注解的核心概念

1. 注解的本質(zhì)

注解本質(zhì)是一個(gè)繼承了 java.lang.annotation.Annotation 接口的特殊接口,編譯器會(huì)自動(dòng)為其生成實(shí)現(xiàn)類。

2. 元注解(Meta-Annotation)

元注解是用于修飾注解的注解,定義了自定義注解的生命周期、作用范圍等屬性。Java 內(nèi)置 4 個(gè)核心元注解:

元注解作用說明
@Retention指定注解的生命周期(必須聲明)
@Target指定注解可作用的元素類型(如類、方法、字段等,必須聲明)
@Documented標(biāo)記注解會(huì)被 javadoc 工具提取到文檔中(可選)
@Inherited標(biāo)記注解可被子類繼承(僅對(duì)類注解有效,可選)
@Repeatable(Java8+)允許注解在同一元素上重復(fù)使用(可選)

二、自定義注解的步驟

1. 定義注解(使用@interface關(guān)鍵字)

格式:

// 元注解
元注解1
元注解2
...
public @interface 注解名 {
    // 注解屬性(本質(zhì)是接口的抽象方法)
    類型 屬性名() default 默認(rèn)值; // 可選默認(rèn)值,無默認(rèn)值則使用時(shí)必須指定
}

關(guān)鍵說明:

  • 注解屬性的類型只能是:基本類型(int/String/boolean 等)、枚舉、注解、數(shù)組(上述類型的數(shù)組)。
  • 若屬性名是 value,且只有一個(gè)屬性時(shí),使用注解可省略屬性名(直接寫值)。
  • 數(shù)組類型的屬性賦值時(shí),若只有一個(gè)元素,可省略 {}。

2. 常用元注解詳解

(1)@Retention:指定生命周期

取值(RetentionPolicy 枚舉):

  • SOURCE:僅保留在源代碼中,編譯時(shí)刪除(如 @Override)。
  • CLASS:保留到編譯后的 .class 文件中,但 JVM 運(yùn)行時(shí)不加載(默認(rèn)值)。
  • RUNTIME:保留到 JVM 運(yùn)行時(shí),可通過反射獲?。ㄗ畛S?,如日志、校驗(yàn)注解)。

(2)@Target:指定作用范圍

取值(ElementType 枚舉):

  • TYPE:作用于類、接口、枚舉。
  • METHOD:作用于方法。
  • FIELD:作用于字段(成員變量)。
  • PARAMETER:作用于方法參數(shù)。
  • CONSTRUCTOR:作用于構(gòu)造器。
  • ANNOTATION_TYPE:作用于注解本身。
  • LOCAL_VARIABLE:作用于局部變量。
  • MODULE:作用于模塊(Java9+)。

三、自定義注解示例

下面通過 3 個(gè)常見場景,演示自定義注解的定義與使用。

示例 1:基礎(chǔ)注解(無屬性)

定義一個(gè)標(biāo)記型注解(僅用于標(biāo)記,無額外屬性)。

1. 定義注解

import java.lang.annotation.*;
// 生命周期:運(yùn)行時(shí)(可通過反射獲?。?
@Retention(RetentionPolicy.RUNTIME)
// 作用范圍:類、方法
@Target({ElementType.TYPE, ElementType.METHOD})
// 生成 javadoc 文檔
@Documented
public @interface LogAnnotation {
    // 無屬性(標(biāo)記型注解)
}

2. 使用注解

// 作用于類
@LogAnnotation
public class UserService {
    // 作用于方法
    @LogAnnotation
    public void addUser(String username) {
        System.out.println("添加用戶:" + username);
    }
}

示例 2:帶屬性的注解(參數(shù)校驗(yàn))

定義一個(gè)用于字段非空校驗(yàn)的注解,支持自定義提示信息。

1. 定義注解

import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD) // 僅作用于字段
@Documented
public @interface NotNull {
    // 注解屬性:提示信息,默認(rèn)值為 "字段不能為空"
    String message() default "字段不能為空";
}

2. 使用注解(標(biāo)記實(shí)體類字段)

public class User {
    @NotNull(message = "用戶名不能為空")
    private String username;
    @NotNull(message = "年齡不能為空")
    private Integer age;
    // getter/setter/構(gòu)造器省略
}

3. 解析注解(通過反射實(shí)現(xiàn)校驗(yàn)邏輯)

import java.lang.reflect.Field;
public class ValidationUtil {
    // 校驗(yàn)對(duì)象的注解字段
    public static void validate(Object obj) throws IllegalAccessException {
        // 獲取對(duì)象的類
        Class<?> clazz = obj.getClass();
        // 獲取所有字段(包括私有字段)
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            // 允許訪問私有字段
            field.setAccessible(true);
            // 判斷字段是否有 @NotNull 注解
            if (field.isAnnotationPresent(NotNull.class)) {
                // 獲取注解實(shí)例
                NotNull notNull = field.getAnnotation(NotNull.class);
                // 獲取字段值
                Object value = field.get(obj);
                // 校驗(yàn)邏輯:值為 null 則拋出異常
                if (value == null) {
                    throw new IllegalArgumentException(notNull.message());
                }
            }
        }
    }
    // 測試
    public static void main(String[] args) throws IllegalAccessException {
        User user1 = new User(null, 20); // 用戶名 null
        try {
            validate(user1);
        } catch (IllegalArgumentException e) {
            System.out.println(e.getMessage()); // 輸出:用戶名不能為空
        }
        User user2 = new User("張三", null); // 年齡 null
        try {
            validate(user2);
        } catch (IllegalArgumentException e) {
            System.out.println(e.getMessage()); // 輸出:年齡不能為空
        }
    }
}

示例 3:重復(fù)注解(Java8+)

允許同一元素上重復(fù)使用注解(如多角色權(quán)限校驗(yàn))。

1. 定義 “容器注解”(用于存放重復(fù)的注解)

import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Roles {
    // 容器注解必須包含一個(gè)返回目標(biāo)注解數(shù)組的屬性(名稱固定為 value)
    Role[] value();
}

2. 定義可重復(fù)注解(使用@Repeatable關(guān)聯(lián)容器注解)

import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Repeatable(Roles.class) // 指定容器注解
public @interface Role {
    // 注解屬性:角色名稱
    String value();
}

3. 使用重復(fù)注解

public class PermissionService {
    // 重復(fù)使用 @Role 注解(等價(jià)于 @Roles({@Role("admin"), @Role("manager")}))
    @Role("admin")
    @Role("manager")
    public void deleteData() {
        System.out.println("刪除數(shù)據(jù)成功");
    }
}

4. 解析重復(fù)注解

import java.lang.reflect.Method;
public class PermissionUtil {
    public static void checkRole(Method method, String userRole) {
        // 判斷方法是否有 @Roles 注解(重復(fù)注解會(huì)被包裝為容器注解)
        if (method.isAnnotationPresent(Roles.class)) {
            Roles roles = method.getAnnotation(Roles.class);
            Role[] roleArr = roles.value();
            // 校驗(yàn)用戶角色是否在允許范圍內(nèi)
            boolean hasPermission = false;
            for (Role role : roleArr) {
                if (role.value().equals(userRole)) {
                    hasPermission = true;
                    break;
                }
            }
            if (!hasPermission) {
                throw new SecurityException("無權(quán)限執(zhí)行該操作");
            }
        }
    }
    // 測試
    public static void main(String[] args) throws NoSuchMethodException {
        Method method = PermissionService.class.getMethod("deleteData");
        // 測試有權(quán)限(admin)
        checkRole(method, "admin");
        System.out.println("admin 執(zhí)行成功");
        // 測試無權(quán)限(user)
        try {
            checkRole(method, "user");
        } catch (SecurityException e) {
            System.out.println(e.getMessage()); // 輸出:無權(quán)限執(zhí)行該操作
        }
    }
}

四、自定義注解的常見應(yīng)用場景

  1. 日志記錄:標(biāo)記需要打印日志的方法,通過 AOP 攔截并記錄請(qǐng)求參數(shù)、返回值、執(zhí)行時(shí)間等。
  2. 權(quán)限校驗(yàn):標(biāo)記需要權(quán)限的接口 / 方法,通過攔截器或 AOP 校驗(yàn)用戶角色。
  3. 參數(shù)校驗(yàn):標(biāo)記實(shí)體類字段,校驗(yàn)非空、長度、格式等(如 Spring 的 @NotNull、@Size)。
  4. 事務(wù)管理:標(biāo)記需要事務(wù)的方法(如 Spring 的 @Transactional)。
  5. 代碼生成:通過注解標(biāo)記類 / 字段,結(jié)合代碼生成工具(如 MyBatis Generator)生成模板代碼。

五、注意事項(xiàng)

  1. 注解本身不包含業(yè)務(wù)邏輯,需通過反射AOP 解析注解并執(zhí)行相應(yīng)邏輯。
  2. @Retention(RetentionPolicy.RUNTIME) 是運(yùn)行時(shí)解析注解的前提,若僅用于編譯時(shí)檢查(如 @Override),可設(shè)為 SOURCE
  3. 注解屬性的默認(rèn)值不能是 null,需指定合理的默認(rèn)值(如空字符串、默認(rèn)枚舉值)。
  4. 重復(fù)注解需配合容器注解使用(Java8+ 特性),低版本需手動(dòng)包裝為容器注解。

通過自定義注解,開發(fā)者可以將通用邏輯(如日志、校驗(yàn))與業(yè)務(wù)邏輯解耦,提高代碼的復(fù)用性和可讀性。實(shí)際開發(fā)中,Spring、MyBatis 等框架大量使用自定義注解簡化配置(如 @Controller、@Mapper),核心原理與本文示例一致。

到此這篇關(guān)于JAVA自定義注解的文章就介紹到這了,更多相關(guān)java自定義注解內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 菜鳥學(xué)習(xí)java設(shè)計(jì)模式之單例模式

    菜鳥學(xué)習(xí)java設(shè)計(jì)模式之單例模式

    這篇文章主要為大家詳細(xì)介紹了java設(shè)計(jì)模式之單例模式的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-11-11
  • IDEA連接mysql數(shù)據(jù)庫報(bào)錯(cuò)的解決方法

    IDEA連接mysql數(shù)據(jù)庫報(bào)錯(cuò)的解決方法

    這篇文章主要介紹了IDEA連接mysql數(shù)據(jù)庫報(bào)錯(cuò)的解決方法,文中有非常詳細(xì)的圖文示例,對(duì)出現(xiàn)Server returns invalid timezone. Go to ‘Advanced‘ tab and set ‘serverTimezone‘ prope報(bào)錯(cuò)的小伙伴們很有幫助喲,需要的朋友可以參考下
    2021-05-05
  • mybatis-generator生成多次重復(fù)代碼問題以及解決

    mybatis-generator生成多次重復(fù)代碼問題以及解決

    在使用MySQL數(shù)據(jù)庫時(shí),如果多個(gè)數(shù)據(jù)庫中存在相同表名,即使在URL中配置了數(shù)據(jù)庫名,也可能導(dǎo)致數(shù)據(jù)互相影響,解決這一問題的方法是在mapper-generator-config.xml文件中添加catalog屬性,明確指定逆向工程代碼所涉及表的數(shù)據(jù)庫名
    2024-10-10
  • Java Lambda和Stream開發(fā)中20個(gè)高頻錯(cuò)誤案例分析與避坑指南

    Java Lambda和Stream開發(fā)中20個(gè)高頻錯(cuò)誤案例分析與避坑指南

    本篇文章將聚焦開發(fā)中最常見、最高頻的20個(gè)問題(含語法、函數(shù)式接口、Stream流、方法引用、并行流5大模塊),每個(gè)問題都配套問題描述+錯(cuò)誤案例+原因分析+正確解法,希望對(duì)大家有所幫助
    2026-04-04
  • JavaWeb中文編碼問題實(shí)例講解

    JavaWeb中文編碼問題實(shí)例講解

    在本篇文章里小編給大家整理的是關(guān)于JavaWeb中文編碼問題方法的相關(guān)知識(shí)點(diǎn)內(nèi)容,需要的朋友們參考下
    2019-09-09
  • 聊聊SpringBoot使用Nacos進(jìn)行服務(wù)注冊發(fā)現(xiàn)與配置管理問題

    聊聊SpringBoot使用Nacos進(jìn)行服務(wù)注冊發(fā)現(xiàn)與配置管理問題

    Nacos支持基于DNS和基于RPC的服務(wù)發(fā)現(xiàn)(可以作為springcloud的注冊中心)、動(dòng)態(tài)配置服務(wù)(可以做配置中心)、動(dòng)態(tài)?DNS?服務(wù)。本文重點(diǎn)給大家介紹SpringBoot使用Nacos進(jìn)行服務(wù)注冊發(fā)現(xiàn)與配置管理,感興趣的朋友一起看看吧
    2022-01-01
  • Java引用類型interface的用法總結(jié)

    Java引用類型interface的用法總結(jié)

    這篇文章主要為大家詳細(xì)介紹了Java中引用類型interface的用法的相關(guān)資料,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Java有一定幫助,感興趣的可以了解一下
    2022-10-10
  • SpringBoot使用ip2region獲取地理位置信息的方法

    SpringBoot使用ip2region獲取地理位置信息的方法

    這篇文章主要介紹了SpringBoot使用ip2region獲取地理位置信息的相關(guān)知識(shí),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-06-06
  • list的4種遍歷方式(實(shí)例講解)

    list的4種遍歷方式(實(shí)例講解)

    下面小編就為大家?guī)硪黄猯ist的4種遍歷方式(實(shí)例講解)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-08-08
  • mybatis注解之@Mapper和@MapperScan的使用

    mybatis注解之@Mapper和@MapperScan的使用

    這篇文章主要介紹了mybatis注解之@Mapper和@MapperScan的使用,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-10-10

最新評(píng)論

孟州市| 长宁县| 揭阳市| 丹巴县| 临清市| 夏津县| 剑川县| 榆林市| 秦安县| 福贡县| 来宾市| 桂东县| 柳州市| 贺州市| 梁河县| 正蓝旗| 敦化市| 贡觉县| 哈巴河县| 桂平市| 金川县| 扶风县| 哈密市| 郓城县| 杭州市| 兴和县| 淳安县| 金昌市| 绿春县| 察哈| 梁河县| 新营市| 德格县| 怀柔区| 河东区| 萨嘎县| 望江县| 千阳县| 吉林省| 雷波县| 乌兰察布市|