Java 注解原理
下面來看看Java中注解是如何實(shí)現(xiàn)的
創(chuàng)建注解類Inter:
?
創(chuàng)建測(cè)試類Test:
?
在程序第二句設(shè)置斷點(diǎn),可以看到:
?
可以看到,注解的實(shí)例是一個(gè)動(dòng)態(tài)代理類的對(duì)象.
要想查看這個(gè)動(dòng)態(tài)代理類,可以在代碼中加
System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");添加系統(tǒng)代理,將其導(dǎo)出為class文件
?
可以看到如下兩個(gè)文件:
?
反編譯$Proxy1.class,如下:
?
可以看到,動(dòng)態(tài)代理類是我們定義的注解實(shí)現(xiàn)類,反編譯Inner.class,如下:
?
可以看到,注解接口繼承了java.lang.annotation.Annotation, 通過查看源碼,該類源碼如下:
?
可以看到, 該類下的方法都被$Proxy1動(dòng)態(tài)代理類實(shí)現(xiàn)了.
到此處,我們已經(jīng)知道Inner注解(接口)是一個(gè)繼承了Annotation接口的特殊接口,而我們通過反射獲取注解時(shí),返回的是Java運(yùn)行時(shí)生成的動(dòng)態(tài)代理對(duì)象$Proxy1,該類就是Inner注解(接口)的具體實(shí)現(xiàn)類。
那么, 代理類是如何處理方法的調(diào)用的呢?
我們知道, 動(dòng)態(tài)代理方法的調(diào)用最終會(huì)傳遞給綁定的InvocationHandler實(shí)例的invoke方法處理。我們可以看看$Proxy1的源碼
?
其中語句調(diào)用了父類的成員變量,其父類為Proxy, 查看該成員變量,如下:
?
可以看到, h對(duì)象類型就是InvocationHandler接口的某個(gè)實(shí)現(xiàn)類
我們?cè)赑roxy類的構(gòu)造方法處設(shè)置斷點(diǎn):
?
通過斷點(diǎn)可以查看h具體是哪個(gè)對(duì)象:
?
可以看到, 該動(dòng)態(tài)代理類為AnnotationInvocationHandler對(duì)象, 查看該類的invoke方法如下:
?
其中的memberValues變量是以方法名為key,以變量為value的, 如下:
?
那么,這個(gè)memberValues變量是從哪來的呢?
?
可以看到,其是在構(gòu)造函數(shù)中進(jìn)行設(shè)置的.
反編譯我們的Test類,看到:
?
所以中間有一個(gè)類,負(fù)責(zé)創(chuàng)建代理對(duì)象AnnotationInvocationHandler, 其將變量從常量池中取出并創(chuàng)建map, 進(jìn)而創(chuàng)建代理對(duì)象, 這個(gè)類就是 AnnotationParser, 在此不細(xì)說了, 感興趣的可以自行斷點(diǎn)調(diào)試查看.
總結(jié)
注解本質(zhì)是一個(gè)繼承了Annotation的特殊接口,其具體實(shí)現(xiàn)類是Java運(yùn)行時(shí)生成的動(dòng)態(tài)代理類。通過代理對(duì)象調(diào)用自定義注解(接口)的方法,會(huì)最終調(diào)用AnnotationInvocationHandler的invoke方法。該方法會(huì)從memberValues這個(gè)Map中索引出對(duì)應(yīng)的值。而memberValues的來源是Java常量池。
總結(jié)
- 上一篇: mysql增加布尔字段_JDBC对MyS
- 下一篇: java美元兑换,(Java实现) 美元