1. 程式人生 > >Java 註解原理

Java 註解原理

下面來看看Java中註解是如何實現的

建立註解類Inter:

Java 註解原理

 

建立測試類Test:

Java 註解原理

 

在程式第二句設定斷點,可以看到:

Java 註解原理

 

可以看到,註解的例項是一個動態代理類的物件.

要想檢視這個動態代理類,可以在程式碼中加

System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

新增系統代理,將其匯出為class檔案

Java 註解原理

 

可以看到如下兩個檔案:

Java 註解原理

 

反編譯$Proxy1.class,如下:

Java 註解原理

 

可以看到,動態代理類是我們定義的註解實現類,反編譯Inner.class,如下:

Java 註解原理

 

可以看到,註解介面繼承了java.lang.annotation.Annotation, 通過檢視原始碼,該類原始碼如下:

Java 註解原理

 

可以看到, 該類下的方法都被$Proxy1動態代理類實現了.

到此處,我們已經知道Inner註解(介面)是一個繼承了Annotation介面的特殊介面,而我們通過反射獲取註解時,返回的是Java執行時生成的動態代理物件$Proxy1,該類就是Inner註解(介面)的具體實現類。

那麼, 代理類是如何處理方法的呼叫的呢?

我們知道, 動態代理方法的呼叫最終會傳遞給繫結的InvocationHandler例項的invoke方法處理。我們可以看看$Proxy1的原始碼

Java 註解原理

 

其中語句呼叫了父類的成員變數,其父類為Proxy, 檢視該成員變數,如下:

Java 註解原理

 

可以看到, h物件型別就是InvocationHandler介面的某個實現類

我們在Proxy類的構造方法處設定斷點:

Java 註解原理

 

通過斷點可以檢視h具體是哪個物件:

Java 註解原理

 

可以看到, 該動態代理類為AnnotationInvocationHandler物件, 檢視該類的invoke方法如下:

Java 註解原理

 

其中的memberValues變數是以方法名為key,以變數為value的, 如下:

Java 註解原理

 

那麼,這個memberValues變數是從哪來的呢?

Java 註解原理

 

可以看到,其是在建構函式中進行設定的.

反編譯我們的Test類,看到:

Java 註解原理

 

所以中間有一個類,負責建立代理物件AnnotationInvocationHandler, 其將變數從常量池中取出並建立map, 進而建立代理物件, 這個類就是 AnnotationParser, 在此不細說了, 感興趣的可以自行斷點除錯檢視.


總結

註解本質是一個繼承了Annotation的特殊介面,其具體實現類是Java執行時生成的動態代理類。通過代理物件呼叫自定義註解(介面)的方法,會最終呼叫AnnotationInvocationHandler的invoke方法。該方法會從memberValues這個Map中索引出對應的值。而memberValues的來源是Java常量池。