Java 注解底層邏輯流程分析
Java 注解底層邏輯
一、注解的本質(zhì)
注解的類型本質(zhì)上是一個特殊接口,Java 語法強(qiáng)制規(guī)定所有注解類型都會自動繼承 java.lang.annotation.Annotation 接口。
但是,雖然注解繼承的是接口,但注解并沒有普通接口的特性。在注解中,所謂的方法并不是真正的方法,而是類似于定義變量,用來聲明注解能接收哪些類型的配置項(xiàng),例如:
public @interface MyAnno {
String value();
int age() default 18;
}
在上面的注解定義中,MyAnno 可以接收一個字符串類型的配置項(xiàng) value,以及一個整型的配置項(xiàng) age,其中 age 的默認(rèn)值為 18。
這些配置項(xiàng)會被編譯器寫進(jìn)字節(jié)碼的注解表中,存儲為鍵值對。運(yùn)行時 JVM 會通過動態(tài)代理生成一個實(shí)現(xiàn)了該注解接口的對象,當(dāng)調(diào)用 anno.value() 時,其實(shí)是從注解表中取出對應(yīng)的配置值。
從這里可以看出,接口只是注解的類型表現(xiàn)形式,并不具備接口的繼承和約束作用。實(shí)際上,在字節(jié)碼層面,注解是一種作用在類、方法、字段、參數(shù)等上的 元數(shù)據(jù)。元數(shù)據(jù)是“描述數(shù)據(jù)的數(shù)據(jù)”,在 Excel 表格中,表頭、格式就是元數(shù)據(jù);在 Java 中,類名、字段列表、方法簽名、訪問控制符、變量名和變量類型、方法返回類型和參數(shù)列表等,都是元數(shù)據(jù)。簡而言之,注解是一種“標(biāo)簽”,用來描述數(shù)據(jù)。
二、注解的讀取邏輯
注解本身不是一個動作,只有被讀取后才會發(fā)揮作用。讀取注解的“執(zhí)行者”可以是:
- 編譯器:例如
@Override會在編譯階段被讀取,觸發(fā)方法重寫檢查。 - 運(yùn)行時框架:例如 Spring 中的
@Autowired,會在運(yùn)行時通過反射掃描注解并執(zhí)行依賴注入邏輯。 - 注解處理器(APT):例如 Lombok 的
@Data,在編譯階段通過 APT 直接解析源代碼/字節(jié)碼,生成額外的代碼。
也就是說,注解是一個標(biāo)識,被相應(yīng)的讀取者掃描讀取后,才會觸發(fā)相應(yīng)的操作。
那么注解掃描是如何做到的呢?本質(zhì)就是:
- 找到目標(biāo)類(例如限定的包路徑下的類)。
- 使用反射或字節(jié)碼解析工具讀取元數(shù)據(jù)。
- 判斷是否存在目標(biāo)注解,并執(zhí)行相應(yīng)邏輯。
對于大型框架來說,掃描所有類代價過高,所以通常會:
- 限定包路徑(如 Spring Boot 默認(rèn)只掃描啟動類所在包)。
- 使用懶加載。
- 使用緩存避免重復(fù)掃描。
- 借助字節(jié)碼工具(如 ASM)直接讀取
.class文件里的注解表,而不是提前加載類。
三、注解的字節(jié)碼存儲
Java 編譯器在編譯過程中會掃描所有注解,并將其記錄到 .class 文件中的注解屬性表中。
注解信息會根據(jù)作用位置,存放在不同的結(jié)構(gòu)的 attributes[] 數(shù)組中:
- 類注解:存放在
ClassFile的attributes中。 - 字段注解:存放在
field_info的attributes中。 - 方法注解:存放在
method_info的attributes中。 - 方法參數(shù)注解:存放在
RuntimeVisibleParameterAnnotations或RuntimeInvisibleParameterAnnotations中。
注解在 .class 文件里的存儲結(jié)構(gòu)大致為:
annotation {
u2 type_index; // 注解類型的常量池引用,例如 "Lcom/example/MyAnno;"
u2 num_element_value_pairs; // 注解屬性數(shù)量
{
u2 element_name_index; // 屬性名常量池索引,例如 "value"
element_value value; // 屬性值(常量池索引或字面量)
} num_element_value_pairs;
}
在運(yùn)行時,如果注解的保留策略是 RUNTIME,JVM 會將這些注解信息加載到內(nèi)存中,并通過反射 API 提供訪問接口,例如:
Class<?> clazz = Demo.class; MyAnno anno = clazz.getAnnotation(MyAnno.class); System.out.println(anno.value()); // 輸出配置的值
這樣就完成了從源碼 → 編譯 → 運(yùn)行的整個流程。
到此這篇關(guān)于Java 注解底層邏輯的文章就介紹到這了,更多相關(guān)java注解底層內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
mybatis?plus?MetaObjectHandler?不生效的解決
今天使用mybatis-plus自動為更新和插入操作插入更新時間和插入時間,配置了MetaObjectHandler不生效,本文就來解決一下,具有一定的 參考價值,感興趣的可以了解一下2023-10-10
基于CyclicBarrier和CountDownLatch的使用區(qū)別說明
這篇文章主要介紹了基于CyclicBarrier和CountDownLatch的使用區(qū)別說明,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-09-09
Maven入門之使用Nexus搭建Maven私服及上傳下載jar包
這篇文章主要介紹了Maven入門之使用Nexus搭建Maven私服及上傳下載jar包,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-12-12
SpringBoot整合Spring Security構(gòu)建安全的Web應(yīng)用
pring Security是一個強(qiáng)大的身份驗(yàn)證和訪問控制框架,本文主要介紹了SpringBoot整合Spring Security構(gòu)建安全的Web應(yīng)用,具有一定的參考價值,感興趣的可以了解一下2024-01-01
Spring MVC 文件、cookies的接收 與REST響應(yīng)詳
在SpringMVC中,使用@RequestPart注解可接收文件并處理多部分請求,同時可以通過@CookieValue和HttpServletResponse來獲取和設(shè)置Cookies,本文介紹Spring MVC 文件、cookies的接收 與REST響應(yīng),感興趣的朋友跟隨小編一起看看吧2024-09-09

