Java注解示例詳解(含底層原理)
一、什么是注解?
定義注解:注解是Java語言的元數(shù)據(jù)(在Java層面,所有代碼都可視為數(shù)據(jù),而注解就是為代碼添加特定數(shù)據(jù)的機(jī)制),用于修飾代碼元素(類、方法、字段等)。它本身不直接影響程序運(yùn)行,需要通過工具(編譯器、運(yùn)行時(shí)環(huán)境、反射API)解析處理,實(shí)現(xiàn)編譯檢查、代碼生成、運(yùn)行時(shí)配置等功能。
注解的價(jià)值:簡化傳統(tǒng)的XML配置方式、減少模板代碼、提高代碼可讀性、實(shí)現(xiàn)邏輯與配置解耦
與注釋的區(qū)別:
- 替代傳統(tǒng)XML配置方式
- 減少重復(fù)代碼
- 提升代碼可讀性
- 實(shí)現(xiàn)邏輯與配置分離
| 維度 | 注解(Annotation) | 注釋(Comment) |
|---|---|---|
| 作用對象 | 程序(編譯器、框架可解析) | 開發(fā)者(僅用于閱讀) |
| 語法規(guī)范 | 有嚴(yán)格的語法(需用@interface定義) | 無語法限制(// 或 /* */包裹) |
| 運(yùn)行時(shí)影響 | 可通過反射API獲取,影響程序邏輯 | 編譯時(shí)被忽略,無任何影響 |
注解的本質(zhì):注解的本質(zhì)實(shí)際上是一個(gè)繼承自 java.lang.annotation.Annotation 的接口
注解的處理時(shí)機(jī):
- 編譯期間:編譯器根據(jù)注解處理器(一個(gè)繼承了 javax.annotation.processing.AbstractProcessor 抽象類的類,該類由 javac 編譯器進(jìn)行讀取),可以在編譯期間對注解進(jìn)行處理(代碼生成)
- 運(yùn)行期間:通過反射機(jī)制獲取注解(此時(shí) JVM 用動(dòng)態(tài)代理為該注解生成了一個(gè)的代理類),可以在運(yùn)行是獲取注解的信息
二、標(biāo)記注解的注解——元注解
元注解本質(zhì)上是給 Java 工具鏈(編譯器、JVM)“看” 的規(guī)則說明,用于告訴這些工具:“這個(gè)注解應(yīng)該被如何處理?它能修飾什么?能保留到什么時(shí)候”。
Java 定義了 5 個(gè)標(biāo)準(zhǔn)的元注解類型且都位于 java.lang.annotation 包下:
@Target
用于指定注解可以應(yīng)用的Java元素類型,例如類、方法、字段,屬性的值由枚舉類 java.lang.annotation.ElementType 提供:
| 字段 | 說明 |
|---|---|
| TYPE | 用于類、接口、注解、枚舉 |
| FIELD | 用于字段(類的成員變量),或者枚舉常量 |
| METHOD | 用于方法 |
| PARAMETER | 用于方法或者構(gòu)造方法的參數(shù) |
| CONSTRUCTOR | 用于構(gòu)造方法 |
| LOCAL_VARIABLE | 用于變量 |
| ANNOTATION_TYPE | 用于注解 |
| PACKAGE | 用于包 |
| TYPE_PARAMETER | 用于泛型參數(shù) |
| TYPE_USE | 用于聲明語句、泛型或者強(qiáng)制轉(zhuǎn)換語句中的類型 |
| MODULE | 用于模塊 |
@Retention
用于指定注解的保留策略,即注解在哪個(gè)階段保留,屬性的值由枚舉類 java.lang.annotation.RetentionPolicy 提供:
| 字段 | 說明 |
|---|---|
| SOURCE | 會(huì)被編譯器丟棄,不會(huì)出現(xiàn)在class文件中。 |
| CLASS | 默認(rèn)值,會(huì)被保留在class文件中,但 JVM 加載類時(shí)不會(huì)處理它們。 |
| RUNTIME | 會(huì)被保留在class文件中,還會(huì)被JVM加載,因此可以通過反射機(jī)制在運(yùn)行時(shí)訪問。這也是注解生命周期中最常用的一種策略 |
@Documented
用于標(biāo)注其他注解,使其在生成 Javadoc 文檔時(shí)被包含在內(nèi)。
@Inherited
用于標(biāo)注其他注解,被標(biāo)記的注解具有繼承性。
當(dāng)被標(biāo)記的注解用在一個(gè)類上,那么該類的子類可以繼承這個(gè)被標(biāo)記的注解。
注意:@Inherited 僅對類級別的注解有效,對方法、字段、參數(shù)等其他程序元素的注解無繼承效果。
示例:
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited // 僅對類上的注解有效,方法和字段上的該注解不會(huì)被繼承
public @interface MyAnnotation { ... }@Repeatable
用于標(biāo)記一個(gè)注解可以在同一個(gè)程序元素上重復(fù)使用。
使用時(shí)需要指定一個(gè) “容器注解”(該容器注解的屬性是當(dāng)前注解的數(shù)組)。
示例:
// 容器注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotations {
MyAnnotation[] value();
}
// 重復(fù)注解(使用@Repeatable標(biāo)記)
@Repeatable(MyAnnotations.class)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String value();
}
// 使用示例
@MyAnnotation("a")
@MyAnnotation("b")
public class MyClass {}三、自定義注解
定義注解,使用 @interface 關(guān)鍵字,基本結(jié)構(gòu):
import java.lang.annotation.*;
// 元注解:指定注解的適用范圍(類、方法、字段等)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
// 元注解:指定注解的生命周期(SOURCE/CLASS/RUNTIME)
@Retention(RetentionPolicy.RUNTIME)
// 元注解:是否被javadoc文檔提取
@Documented
// 元注解:是否允許子類繼承該注解(僅對類注解有效)
@Inherited
public @interface MyAnnotation {
// 注解屬性(類似接口方法,但可指定默認(rèn)值)
String value(); // 必選屬性(無默認(rèn)值)
int count() default 1; // 可選屬性(有默認(rèn)值)
String[] tags() default {}; // 數(shù)組類型
}注解屬性的類型限制:
- 基本數(shù)據(jù)類型
- String、Class、枚舉
- 其他注解
- 以上類型的數(shù)組
- 如果使用其他類型,編譯時(shí)會(huì)直接報(bào)錯(cuò)
"value" 屬性的特殊性:
當(dāng)注解有且僅有`value`一個(gè)屬性時(shí),使用時(shí)可省略屬性名
// 定義僅含value屬性的注解
public @interface MyAnnot {
String value();
}
// 使用時(shí)可簡化
@MyAnnot("test") // 等價(jià)于 @MyAnnot(value = "test")
public class Demo {}當(dāng)注解有多個(gè)屬性,但僅需指定`value`時(shí),仍可省略屬性名
public @interface MyAnnot {
String value();
int count() default 1; // 有默認(rèn)值
}
// 僅指定value,可省略屬性名
@MyAnnot("test") // 等價(jià)于 @MyAnnot(value = "test", count = 1)
public class Demo {}若需指定多個(gè)屬性,`value`不能省略屬性名
public @interface MyAnnot {
String value();
int count() default 1;
}
// 錯(cuò)誤寫法:多屬性時(shí)不能省略value=
// @MyAnnot("test", count = 2)
// 正確寫法:必須顯式指定value=
@MyAnnot(value = "test", count = 2)
public class Demo {}四、反射 API 獲取注解信息
4.1 可訪問注解信息的核心類
| 類 | 說明 |
|---|---|
java.lang.Class | 表示類、接口、枚舉等類型,可訪問類本身、父類、實(shí)現(xiàn)接口上的注解。 |
java.lang.reflect.Field | 表示類的字段,可訪問字段上的注解。 |
java.lang.reflect.Method | 表示類的方法,可訪問方法本身、方法參數(shù)、方法返回值上的注解。 |
java.lang.reflect.Constructor | 表示類的構(gòu)造函數(shù),可訪問構(gòu)造函數(shù)本身及參數(shù)上的注解。 |
java.lang.reflect.Parameter | 表示方法或構(gòu)造函數(shù)的參數(shù),可訪問參數(shù)上的注解。 |
java.lang.reflect.AnnotatedType | 表示被注解的類型(如泛型類型、數(shù)組類型等)。 |
4.2 反射 API 中獲取注解的核心方法
4.2.1 Class 類的注解方法
| 方法 | 說明 |
|---|---|
getAnnotation(Class<T> annotationClass) | 獲取該類上指定類型的注解(若注解被@Inherited元注解標(biāo)記,則包括從父類繼承的注解),不存在則返回null。 |
getAnnotations() | 獲取該類上的所有注解(包括繼承的)。 |
getDeclaredAnnotation(Class<T> annotationClass) | 獲取該類上直接聲明的指定類型注解(不包括繼承的)。 |
getDeclaredAnnotations() | 獲取該類上直接聲明的所有注解(不包括繼承的)。 |
isAnnotationPresent(Class<? extends Annotation> annotationClass) | 判斷該類是否存在指定類型的注解(包括繼承的)。 |
4.2.2 Field 類的注解方法
| 方法 | 說明 |
|---|---|
getAnnotation(Class<T> annotationClass) | 獲取該字段上指定類型的注解。 |
getAnnotations() | 獲取該字段上的所有注解。 |
getDeclaredAnnotations() | 獲取該字段上直接聲明的所有注解(因字段注解不可繼承,故結(jié)果與getAnnotations()一致)。 |
isAnnotationPresent(Class<? extends Annotation> annotationClass) | 判斷該字段是否存在指定類型的注解。 |
4.2.3 Method 類的注解方法
| 方法 | 說明 |
|---|---|
getAnnotation(Class<T> annotationClass) | 獲取該方法上指定類型的注解。 |
getAnnotations() | 獲取該方法上的所有注解。 |
getDeclaredAnnotations() | 同getAnnotations()(方法注解不可繼承)。 |
isAnnotationPresent(Class<? extends Annotation> annotationClass) | 判斷該方法是否存在指定類型的注解。 |
getParameterAnnotations() | 獲取該方法所有參數(shù)上的注解(返回二維數(shù)組,每個(gè)元素是一個(gè)參數(shù)的注解數(shù)組)。 |
getAnnotationsByType(Class<T> annotationClass) | 獲取該方法上指定類型的所有注解(支持重復(fù)注解)。 |
4.2.4 Constructor 類的注解方法
| 方法 | 說明 |
|---|---|
getAnnotation(Class<T> annotationClass) | 獲取該構(gòu)造函數(shù)上指定類型的注解。 |
getAnnotations() | 獲取該構(gòu)造函數(shù)上的所有注解。 |
getDeclaredAnnotations() | 同getAnnotations()(構(gòu)造函數(shù)注解不可繼承)。 |
getParameterAnnotations() | 獲取該構(gòu)造函數(shù)所有參數(shù)上的注解(返回二維數(shù)組)。 |
4.2.4 Parameter 類的注解方法
| 方法 | 說明 |
|---|---|
getAnnotation(Class<T> annotationClass) | 獲取該參數(shù)上指定類型的注解。 |
getAnnotations() | 獲取該參數(shù)上的所有注解。 |
getDeclaredAnnotations() | 同getAnnotations()(參數(shù)注解不可繼承)。 |
isAnnotationPresent(Class<? extends Annotation> annotationClass) | 判斷該參數(shù)是否存在指定類型的注解。 |
4.2.5 AnnotatedType 類的注解方法
| 方法 | 說明 |
|---|---|
getAnnotation(Class<T> annotationClass) | 獲取指定類型的注解(若存在)。 |
getAnnotations() | 返回該類型上的所有注解(包括繼承的)。 |
getDeclaredAnnotations() | 返回該類型上直接聲明的注解(不包括繼承的)。 |
getAnnotationsByType(Class<T> annotationClass) | 獲取指定類型的所有注解(包括重復(fù)注解)。 |
Type getType() | 返回當(dāng)前 AnnotatedType 所表示的原始類型(如 List<String> 對應(yīng)的 Type 對象)。 |
五、代碼示例
自定義日志注解,用于標(biāo)記需要記錄日志的方法
import java.lang.annotation.*;
/**
* 自定義日志注解
* 用于標(biāo)記需要記錄日志的方法
*/
@Target(ElementType.METHOD) // 僅用于方法
@Retention(RetentionPolicy.RUNTIME) // 運(yùn)行時(shí)保留,可通過反射獲取
@Documented // 生成文檔時(shí)包含該注解
public @interface Log {
// 操作描述(value屬性,可簡化使用)
String value() default "";
// 是否記錄參數(shù)
boolean recordParams() default true;
// 是否記錄返回值
boolean recordResult() default false;
}
運(yùn)行時(shí)注解解析器,通過動(dòng)態(tài)代理實(shí)現(xiàn)日志記錄功能
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
/**
* 運(yùn)行時(shí)注解解析器
* 通過動(dòng)態(tài)代理實(shí)現(xiàn)日志記錄功能
*/
public class LogRuntimeParser implements InvocationHandler {
// 目標(biāo)對象
private final Object target;
public LogRuntimeParser(Object target) {
this.target = target;
}
// 創(chuàng)建代理對象
public static Object createProxy(Object target) {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new LogRuntimeParser(target)
);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 檢查方法是否有@Log注解
if (method.isAnnotationPresent(Log.class)) {
Log log = method.getAnnotation(Log.class);
// 記錄開始時(shí)間
long startTime = System.currentTimeMillis();
// 打印日志信息
System.out.println("\n===== 日志開始 =====");
System.out.println("操作描述: " + log.value());
System.out.println("方法名稱: " + method.getName());
// 如果需要記錄參數(shù)
if (log.recordParams() && args != null) {
System.out.println("參數(shù)列表: " + Arrays.toString(args));
}
// 執(zhí)行目標(biāo)方法
Object result = method.invoke(target, args);
// 如果需要記錄返回值
if (log.recordResult()) {
System.out.println("返回結(jié)果: " + result);
}
// 記錄執(zhí)行時(shí)間
System.out.println("執(zhí)行時(shí)間: " + (System.currentTimeMillis() - startTime) + "ms");
System.out.println("===== 日志結(jié)束 =====");
return result;
}
// 沒有@Log注解的方法,直接執(zhí)行
return method.invoke(target, args);
}
}用戶服務(wù)接口,用于動(dòng)態(tài)代理
/**
* 用戶服務(wù)接口,用于動(dòng)態(tài)代理
*/
public interface UserServiceInterface {
boolean login(String username, String password);
String register(String username, String email);
void updateProfile(String username, String newEmail);
}
業(yè)務(wù)服務(wù)類,使用自定義的@Log注解
/**
* 業(yè)務(wù)服務(wù)類,使用自定義的@Log注解
*/
public class UserService {
// 使用注解,僅指定value(可簡化寫法)
@Log("用戶登錄")
public boolean login(String username, String password) {
System.out.println("執(zhí)行登錄邏輯...");
return "admin".equals(username) && "123456".equals(password);
}
// 完整指定注解的所有屬性
@Log(value = "用戶注冊", recordParams = true, recordResult = true)
public String register(String username, String email) {
System.out.println("執(zhí)行注冊邏輯...");
return "注冊成功,用戶ID: " + System.currentTimeMillis();
}
// 不使用注解的方法(不會(huì)被日志記錄)
public void updateProfile(String username, String newEmail) {
System.out.println("執(zhí)行更新資料邏輯...");
}
}測試主類
/**
* 測試主類
*/
public class Main {
public static void main(String[] args) {
// 創(chuàng)建目標(biāo)對象
UserService userService = new UserService();
// 創(chuàng)建代理對象(用于運(yùn)行時(shí)解析注解)
UserServiceInterface proxy = (UserServiceInterface) LogRuntimeParser.createProxy(userService);
// 調(diào)用被@Log注解的方法
proxy.login("admin", "123456");
proxy.register("testUser", "test@example.com");
// 調(diào)用未被@Log注解的方法
proxy.updateProfile("admin", "new@example.com");
}
}六、底層原理
編譯時(shí)處理注解的過程:
編譯時(shí)處理注解實(shí)際上是通過一個(gè)實(shí)現(xiàn)了 javax.annotation.processing.AbstractProcessor 抽象類的類來進(jìn)行處理的,具體的操作可以參考網(wǎng)址:
運(yùn)行時(shí)處理注解的過程:
先來看一段代碼,一段自定義的注解源代碼,注解當(dāng)中包含一個(gè) value() 屬性。代碼如下:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author NanJi
* @version 1.0
* @annotationName InitMethod
* @desc 自定義的初始化方法注解
* @date 2025/8/2: 11:20
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface InitMethod {
String value() default "";
}經(jīng)過Java編譯器編譯之后的代碼,再反編譯回來是什么樣子呢?代碼如下:
// 編譯后的 InitMethod.class
public interface InitMethod extends java.lang.annotation.Annotation {
public abstract String value(); // 對應(yīng)注解的value屬性
// 編譯器自動(dòng)添加:返回注解類型
Class<? extends Annotation> annotationType();
}可以看到,定義的 InitMethod 注解實(shí)際上是一個(gè)繼承了 java.lang.annotation.Annotation 接口的接口。接下來我們來看一下運(yùn)行時(shí)訪問注解發(fā)生了什么?
目錄結(jié)構(gòu)如下:

同樣以 InitMethod 為例:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author NanJi
* @version 1.0
* @annotationName InitMethod
* @desc 自定義的初始化方法注解
* @date 2025/8/2: 11:20
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface InitMethod {
String value() default "";
}在定義一個(gè) InitDemo 類,用來測試 InitMethod 注解,代碼如下:
/**
* @author NanJi
* @version 1.0
* @className InitDemo
* @desc 測試初始化方法注解
* @date 2025/8/2 : 11:23
*/
public class InitDemo {
@InitMethod("init...")
public void init() {
System.out.println("正在初始化...");
}
}接著定義一個(gè)測試類 Main,代碼如下:
import java.lang.reflect.*;
/**
* @author NanJi
* @version 1.0
* @className Main
* @desc 測試類
* @date 2025/8/2: 11:30
*/
public class Main {
public static void main(String[] args) throws Exception {
// 寫入代理類文件
System.setProperty("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");
Class<InitDemo> cls = InitDemo.class;
// 獲取 InitDemo 類的所有方法
Method[] methods = cls.getMethods();
// 遍歷所有方法
for (Method method : methods) {
// 判斷方法是否有 InitMethod 注解
boolean isInitMethod = method.isAnnotationPresent(InitMethod.class);
// 如果有 InitMethod 注解,則獲取當(dāng)前的注解代理類
if (isInitMethod) {
// 獲取 InitMethod 注解的代理類
InitMethod initMethod = method.getAnnotation(InitMethod.class);
// 獲取代理類的類實(shí)例
System.out.println(initMethod.getClass());
// 獲取注解的值,實(shí)際上是通過代理類調(diào)用了注解的 value() 方法
String value = initMethod.value();
// 打印注解的 value() 方法的值
System.out.println("InitMethod value: " + value);
// 調(diào)用 InitDemo 類的 init() 方法
method.invoke(cls.getConstructor().newInstance());
}
}
}
}現(xiàn)在我們來運(yùn)行一下這段程序,看看使用 InitMethod 這個(gè)注解發(fā)生了什么

可以看到控制臺打印了三條語句:
第一條 class jdk.proxy2.$Proxy1 實(shí)際上就是生成的代理類。
第二條 InitMethod value: init... 實(shí)際上是通過代理類調(diào)用了 InitMethod 注解類的 value() 方法。
第三條 正在初始化... 實(shí)際上是通過反射調(diào)用了 InitDemo 類的 init() 方法。
那生成的代理類在哪里呢?類中的內(nèi)容又有什么呢?接著往下看:
在運(yùn)行測試類的 main 方法時(shí),方法中的第一行代碼:
// 寫入代理類文件
System.setProperty("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");這行代碼的作用就是允許代理類的class文件寫入你的磁盤當(dāng)中,默認(rèn)是寫入到你的項(xiàng)目模塊中,會(huì)生成如下目錄:

接著我們打開 jdk 目錄下最后面的 proxy2 目錄下的 $Proxy1 類,通過idea打開后查看類中的結(jié)構(gòu):
package jdk.proxy2;
import com.ktjiaoyu.annotation.demo.init.InitMethod;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy1 extends Proxy implements InitMethod {
private static final Method m0;
private static final Method m1;
private static final Method m2;
private static final Method m3;
private static final Method m4;
public $Proxy1(InvocationHandler var1) {
super(var1);
}
public final int hashCode() {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final boolean equals(Object var1) {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String toString() {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String value() {
try {
return (String)super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final Class annotationType() {
try {
return (Class)super.h.invoke(this, m4, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("com.ktjiaoyu.annotation.demo.init.InitMethod").getMethod("value");
m4 = Class.forName("com.ktjiaoyu.annotation.demo.init.InitMethod").getMethod("annotationType");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(((Throwable)var2).getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(((Throwable)var3).getMessage());
}
}
private static MethodHandles.Lookup proxyClassLookup(MethodHandles.Lookup var0) throws IllegalAccessException {
if (var0.lookupClass() == Proxy.class && var0.hasFullPrivilegeAccess()) {
return MethodHandles.lookup();
} else {
throw new IllegalAccessException(var0.toString());
}
}
}這個(gè)類是通過反射 API 獲取注解時(shí)由JVM實(shí)現(xiàn)的,在這個(gè)類的靜態(tài)代碼塊中為成員變量 m3 附了值,這個(gè)值實(shí)際上就是 InitMethod 注解中的 value() 方法。
static {
try {
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("com.ktjiaoyu.annotation.demo.init.InitMethod").getMethod("value");
m4 = Class.forName("com.ktjiaoyu.annotation.demo.init.InitMethod").getMethod("annotationType");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(((Throwable)var2).getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(((Throwable)var3).getMessage());
}
}在我們調(diào)用 value() 方法時(shí),實(shí)際上是調(diào)用的代理類中的 value() 方法,代碼如下:
public final String value() {
try {
return (String)super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}這個(gè) value() 方法就是獲取注解 value 屬性的值。到這里就清楚了,為什么可以通過反射去拿到注解的值。
七、總結(jié)
- 注解的作用是用來描述和標(biāo)記Java代碼當(dāng)中的元素(類、方法、字段等)
- 元注解是用來標(biāo)記注解的注解,作用是給 Java 工具鏈(編譯器、JVM、注解處理器)去識別的規(guī)則,Java 工具鏈根據(jù)對應(yīng)的規(guī)則進(jìn)行對應(yīng)的處理
- 注解的本質(zhì)實(shí)際上是一個(gè)實(shí)現(xiàn)了 java.lang.annotation.Annotation 接口的接口
- 注解的處理時(shí)機(jī)發(fā)生在編譯期,由一個(gè)實(shí)現(xiàn)了 javax.annotation.processing.AbstractProcessor 抽象類的類來進(jìn)行操作
- 注解的處理時(shí)機(jī)發(fā)生在運(yùn)行期,通過Java動(dòng)態(tài)代理,去實(shí)現(xiàn)注解當(dāng)中定義的方法,從而獲取對應(yīng)的值
- 通過 System.setProperty("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true") 方法,設(shè)置JVM的系統(tǒng)屬性,告訴JVM將生成的動(dòng)態(tài)代理類保存到文件系統(tǒng)中,方便開發(fā)者查看和調(diào)試。
到此這篇關(guān)于Java注解詳解的文章就介紹到這了,更多相關(guān)Java注解詳解內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
springboot連接neo4j報(bào)錯(cuò)的解決方案
這篇文章主要介紹了springboot連接neo4j報(bào)錯(cuò)的解決方案,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-02-02
java線程池對象ThreadPoolExecutor的深入講解
在我們的開發(fā)中“池”的概念并不罕見,有數(shù)據(jù)庫連接池、線程池、對象池、常量池等等。下面這篇文章主要給大家介紹了關(guān)于java線程池對象ThreadPoolExecutor的相關(guān)資料,需要的朋友可以參考借鑒,下面來一起看看吧2018-09-09
elasticsearch分布式及數(shù)據(jù)的功能源碼分析
這篇文章主要為大家介紹了elasticsearch分布式及數(shù)據(jù)功能源碼分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-04-04
阿里nacos+springboot+dubbo2.7.3統(tǒng)一處理異常的兩種方式
本文主要介紹了阿里nacos+springboot+dubbo2.7.3統(tǒng)一處理異常的兩種方式,文中根據(jù)實(shí)例編碼詳細(xì)介紹的十分詳盡,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03

