JAVA自定義注解的步驟示例詳解
簡介
在 Java 中,注解(Annotation) 是一種特殊的元數(shù)據(jù)(Metadata),可以標記在類、方法、字段、參數(shù)等元素上,用于傳遞額外信息。自定義注解允許開發(fā)者根據(jù)業(yè)務需求定義自己的注解,結(jié)合反射、AOP 等技術實現(xiàn)靈活的功能(如日志記錄、權限校驗、參數(shù)校驗等)。
一、注解的核心概念
1. 注解的本質(zhì)
注解本質(zhì)是一個繼承了 java.lang.annotation.Annotation 接口的特殊接口,編譯器會自動為其生成實現(xiàn)類。
2. 元注解(Meta-Annotation)
元注解是用于修飾注解的注解,定義了自定義注解的生命周期、作用范圍等屬性。Java 內(nèi)置 4 個核心元注解:
| 元注解 | 作用說明 |
|---|---|
@Retention | 指定注解的生命周期(必須聲明) |
@Target | 指定注解可作用的元素類型(如類、方法、字段等,必須聲明) |
@Documented | 標記注解會被 javadoc 工具提取到文檔中(可選) |
@Inherited | 標記注解可被子類繼承(僅對類注解有效,可選) |
@Repeatable(Java8+) | 允許注解在同一元素上重復使用(可選) |
二、自定義注解的步驟
1. 定義注解(使用@interface關鍵字)
格式:
// 元注解
元注解1
元注解2
...
public @interface 注解名 {
// 注解屬性(本質(zhì)是接口的抽象方法)
類型 屬性名() default 默認值; // 可選默認值,無默認值則使用時必須指定
}關鍵說明:
- 注解屬性的類型只能是:基本類型(
int/String/boolean等)、枚舉、注解、數(shù)組(上述類型的數(shù)組)。 - 若屬性名是
value,且只有一個屬性時,使用注解可省略屬性名(直接寫值)。 - 數(shù)組類型的屬性賦值時,若只有一個元素,可省略
{}。
2. 常用元注解詳解
(1)@Retention:指定生命周期
取值(RetentionPolicy 枚舉):
SOURCE:僅保留在源代碼中,編譯時刪除(如@Override)。CLASS:保留到編譯后的.class文件中,但 JVM 運行時不加載(默認值)。RUNTIME:保留到 JVM 運行時,可通過反射獲?。ㄗ畛S?,如日志、校驗注解)。
(2)@Target:指定作用范圍
取值(ElementType 枚舉):
TYPE:作用于類、接口、枚舉。METHOD:作用于方法。FIELD:作用于字段(成員變量)。PARAMETER:作用于方法參數(shù)。CONSTRUCTOR:作用于構造器。ANNOTATION_TYPE:作用于注解本身。LOCAL_VARIABLE:作用于局部變量。MODULE:作用于模塊(Java9+)。
三、自定義注解示例
下面通過 3 個常見場景,演示自定義注解的定義與使用。
示例 1:基礎注解(無屬性)
定義一個標記型注解(僅用于標記,無額外屬性)。
1. 定義注解
import java.lang.annotation.*;
// 生命周期:運行時(可通過反射獲?。?
@Retention(RetentionPolicy.RUNTIME)
// 作用范圍:類、方法
@Target({ElementType.TYPE, ElementType.METHOD})
// 生成 javadoc 文檔
@Documented
public @interface LogAnnotation {
// 無屬性(標記型注解)
}2. 使用注解
// 作用于類
@LogAnnotation
public class UserService {
// 作用于方法
@LogAnnotation
public void addUser(String username) {
System.out.println("添加用戶:" + username);
}
}示例 2:帶屬性的注解(參數(shù)校驗)
定義一個用于字段非空校驗的注解,支持自定義提示信息。
1. 定義注解
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD) // 僅作用于字段
@Documented
public @interface NotNull {
// 注解屬性:提示信息,默認值為 "字段不能為空"
String message() default "字段不能為空";
}2. 使用注解(標記實體類字段)
public class User {
@NotNull(message = "用戶名不能為空")
private String username;
@NotNull(message = "年齡不能為空")
private Integer age;
// getter/setter/構造器省略
}3. 解析注解(通過反射實現(xiàn)校驗邏輯)
import java.lang.reflect.Field;
public class ValidationUtil {
// 校驗對象的注解字段
public static void validate(Object obj) throws IllegalAccessException {
// 獲取對象的類
Class<?> clazz = obj.getClass();
// 獲取所有字段(包括私有字段)
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
// 允許訪問私有字段
field.setAccessible(true);
// 判斷字段是否有 @NotNull 注解
if (field.isAnnotationPresent(NotNull.class)) {
// 獲取注解實例
NotNull notNull = field.getAnnotation(NotNull.class);
// 獲取字段值
Object value = field.get(obj);
// 校驗邏輯:值為 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:重復注解(Java8+)
允許同一元素上重復使用注解(如多角色權限校驗)。
1. 定義 “容器注解”(用于存放重復的注解)
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Roles {
// 容器注解必須包含一個返回目標注解數(shù)組的屬性(名稱固定為 value)
Role[] value();
}2. 定義可重復注解(使用@Repeatable關聯(lián)容器注解)
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Repeatable(Roles.class) // 指定容器注解
public @interface Role {
// 注解屬性:角色名稱
String value();
}3. 使用重復注解
public class PermissionService {
// 重復使用 @Role 注解(等價于 @Roles({@Role("admin"), @Role("manager")}))
@Role("admin")
@Role("manager")
public void deleteData() {
System.out.println("刪除數(shù)據(jù)成功");
}
}4. 解析重復注解
import java.lang.reflect.Method;
public class PermissionUtil {
public static void checkRole(Method method, String userRole) {
// 判斷方法是否有 @Roles 注解(重復注解會被包裝為容器注解)
if (method.isAnnotationPresent(Roles.class)) {
Roles roles = method.getAnnotation(Roles.class);
Role[] roleArr = roles.value();
// 校驗用戶角色是否在允許范圍內(nèi)
boolean hasPermission = false;
for (Role role : roleArr) {
if (role.value().equals(userRole)) {
hasPermission = true;
break;
}
}
if (!hasPermission) {
throw new SecurityException("無權限執(zhí)行該操作");
}
}
}
// 測試
public static void main(String[] args) throws NoSuchMethodException {
Method method = PermissionService.class.getMethod("deleteData");
// 測試有權限(admin)
checkRole(method, "admin");
System.out.println("admin 執(zhí)行成功");
// 測試無權限(user)
try {
checkRole(method, "user");
} catch (SecurityException e) {
System.out.println(e.getMessage()); // 輸出:無權限執(zhí)行該操作
}
}
}四、自定義注解的常見應用場景
- 日志記錄:標記需要打印日志的方法,通過 AOP 攔截并記錄請求參數(shù)、返回值、執(zhí)行時間等。
- 權限校驗:標記需要權限的接口 / 方法,通過攔截器或 AOP 校驗用戶角色。
- 參數(shù)校驗:標記實體類字段,校驗非空、長度、格式等(如 Spring 的
@NotNull、@Size)。 - 事務管理:標記需要事務的方法(如 Spring 的
@Transactional)。 - 代碼生成:通過注解標記類 / 字段,結(jié)合代碼生成工具(如 MyBatis Generator)生成模板代碼。
五、注意事項
- 注解本身不包含業(yè)務邏輯,需通過反射或AOP 解析注解并執(zhí)行相應邏輯。
@Retention(RetentionPolicy.RUNTIME)是運行時解析注解的前提,若僅用于編譯時檢查(如@Override),可設為SOURCE。- 注解屬性的默認值不能是
null,需指定合理的默認值(如空字符串、默認枚舉值)。 - 重復注解需配合容器注解使用(Java8+ 特性),低版本需手動包裝為容器注解。
通過自定義注解,開發(fā)者可以將通用邏輯(如日志、校驗)與業(yè)務邏輯解耦,提高代碼的復用性和可讀性。實際開發(fā)中,Spring、MyBatis 等框架大量使用自定義注解簡化配置(如 @Controller、@Mapper),核心原理與本文示例一致。
到此這篇關于JAVA自定義注解的文章就介紹到這了,更多相關java自定義注解內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Spring Boot2.0實現(xiàn)靜態(tài)資源版本控制詳解
這篇文章主要給大家介紹了關于Spring Boot2.0實現(xiàn)靜態(tài)資源版本控制的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2018-11-11
redis setIfAbsent和setnx的區(qū)別與使用說明
這篇文章主要介紹了redis setIfAbsent和setnx的區(qū)別與使用,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08
Java面試重點中的重點之Elasticsearch核心原理
ElasticSearch是一個基于Lucene的搜索引擎,是用Java語言開發(fā)的,能夠達到實時搜索,穩(wěn)定,可靠,快速,安裝使用方便,作為Apache許可條款下的開放源碼發(fā)布,是一種流行的企業(yè)級搜索引擎,是最受歡迎的企業(yè)搜索引擎2022-01-01
java巧用@Convert實現(xiàn)表字段自動轉(zhuǎn)entity
本文主要介紹了java巧用@Convert實現(xiàn)表字段自動轉(zhuǎn)entity,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2024-07-07
Java中基于注解的代碼生成工具MapStruct映射使用詳解
MapStruct?作為一個基于注解的代碼生成工具,為我們提供了一種更加優(yōu)雅、高效的解決方案,本文主要為大家介紹了它的具體使用,感興趣的可以了解下2025-02-02
引入QQ郵箱發(fā)送驗證碼進行安全校驗功能實現(xiàn)
最近遇到這樣的需求用戶輸入自己的郵箱,點擊獲取驗證碼,后臺會發(fā)送一封郵件到對應郵箱中,怎么實現(xiàn)呢?下面小編給大家?guī)砹艘隥Q郵箱發(fā)送驗證碼進行安全校驗功能,需要的朋友可以參考下2023-02-02

