使用反射操作注解
大部分情況下,我們的項目或者開源框架中都定義了大量的注解,而且都是@Retention(RetentionPolicy.RUNTIME)
運行時階段,我們可以通過反射獲取注解中的信息,所以整體遵循下面的一個范式。
- 自定義注解
- 掃描注解
- 通過反射獲取注解的信息,執(zhí)行相應的邏輯。
下面我們重點使用下如何用反射來獲取注解的信息。
- 定義target是注解的注解
@Inherited
@Retention( value = RetentionPolicy.RUNTIME)
@Target(value = {ElementType.ANNOTATION_TYPE})
public @interface AnnoTest {
String value() default "anno";
}
- 定義一個幾乎全量信息的注解
@AnnoTest("alvinAnno")
@Inherited
@Retention( value = RetentionPolicy.RUNTIME)
@Target(value = {ElementType.TYPE_USE,ElementType.PACKAGE,ElementType.FIELD,
ElementType.TYPE_PARAMETER,ElementType.CONSTRUCTOR,ElementType.LOCAL_VARIABLE})
@Documented
public @interface FullAnnoTest {
String value() default "FullAnnoTest";
}
- 定義測試類和反射代碼
@FullAnnoTest("package")
package com.alvin.java.core.anno;
public class ParentObj {
}
@FullAnnoTest("testAnnoReflect")
public class TestAnnoReflect<@FullAnnoTest("parameter") T > extends @FullAnnoTest("parent")ParentObj {
@FullAnnoTest("constructor")
TestAnnoReflect() {
}
//注解字段域
private @FullAnnoTest("name") String name;
//注解泛型字段域
private @FullAnnoTest("value") T value;
//注解通配符
private @FullAnnoTest("list") List<@FullAnnoTest("generic") ?> list;
//注解方法
@FullAnnoTest("method") //注解方法參數(shù)
public String hello(@FullAnnoTest("methodParameter") String name)
throws @FullAnnoTest("Exception") Exception { // 注解拋出異常
//注解局部變量,現(xiàn)在運行時暫時無法獲取(忽略)
@FullAnnoTest("result") String result;
result = "siting";
System.out.println(name);
return result;
}
public static void main(String[] args) throws Exception {
TestAnnoReflect
- 查看對應的執(zhí)行結(jié)果
修飾TestAnnoReflect.class注解value: testAnnoReflect
修飾構(gòu)造器的注解value: constructor
修飾繼承父類的注解value: parent
修飾注解的注解AnnoTest-value: alvinAnno
修飾泛型參數(shù)T注解value: parameter
修飾普通字段域name注解value: name
修飾泛型字段T注解value: value
修飾泛型注解value: list
修飾通配符注解value: generic
修飾方法的注解value: method
修飾方法拋出錯誤的注解value: Exception
修飾方法參數(shù)注解value: methodParameter
修飾package注解value: package
hello
注解的本質(zhì)和底層實現(xiàn)
大家有沒有想過注解的本質(zhì)是什么?
我們先通過反編譯查看注解生成的字節(jié)碼,可以通過javap -v FullAnnoTest.class
查看如下:
可以看到,我們的注解是繼承自Annotation接口。
public interface Annotation {
boolean equals(Object obj);
int hashCode();
String toString();
/**
* Returns the annotation type of this annotation.
* @return the annotation type of this annotation
*/
Class? extends Annotation annotationType();
}
所以注解相當于一個語法糖一樣,可以方便我們使用,本質(zhì)上它是繼承自Annotation
的一個接口。
那大家有沒有想過它的實現(xiàn)類在哪里?比如下面的代碼,獲取到注解,按照上面的解釋,它是一個接口,那調(diào)用value()方法時,它具體調(diào)用的哪個實現(xiàn)類呢?我們并沒有寫實現(xiàn)類啊.....
答案當然就是動態(tài)代理生成的實現(xiàn)類。
AnnoTest annoTest = testTmp.annotationType().getAnnotation(AnnoTest.class);
System.out.println("修飾注解的注解AnnoTest-value: "+annoTest.value());
我們可以在啟動參數(shù)添加如下命令可以查看生成的代理類:-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true
執(zhí)行后,生成代理類如下,
代理大致的代碼如下:
public final class $Proxy2 extends Proxy implements FullAnnoTest {
private static Method m1;
private static Method m2;
private static Method m4;
private static Method m0;
private static Method m3;
public $Proxy2(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final Class annotationType() throws {
try {
return (Class)super.h.invoke(this, m4, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String value() throws {
try {
return (String)super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m4 = Class.forName("com.alvin.java.core.anno.FullAnnoTest").getMethod("annotationType");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
m3 = Class.forName("com.alvin.java.core.anno.FullAnnoTest").getMethod("value");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
我們看value()
方法,這里調(diào)用了super.h對象,也就是InvocationHandler
對象,而我們注解用的是AnnotationInvocationHandler
這個子類,我們在invoke方法中打個斷點,就明白了~~
-
JAVA
+關(guān)注
關(guān)注
19文章
2976瀏覽量
105212 -
JDK
+關(guān)注
關(guān)注
0文章
82瀏覽量
16637 -
spring框架
+關(guān)注
關(guān)注
0文章
7瀏覽量
2055
發(fā)布評論請先 登錄
相關(guān)推薦
Java中注解的作用
![<b class='flag-5'>Java</b><b class='flag-5'>中注解</b>的作用](https://file1.elecfans.com/web2/M00/A5/90/wKgaomUMCNGASXqAAAAb0yxIL-8338.jpg)
如何通過注解來優(yōu)化我們的Java代碼
詳細介紹了Java泛型、注解、并發(fā)編程
HarmonyOS注解的使用方法分享
斬波型運放減少噪聲 怎么做到的?
![斬波型運放減少噪聲 <b class='flag-5'>怎么做到</b>的?](https://file1.elecfans.com//web2/M00/A6/B5/wKgZomUMP9yAPtxrAAALkRs4-AY793.jpg)
分析java注解基本概念
Spring Boot常用注解與使用方式
注解定義Bean及開發(fā)
JAVA中注解是怎么做到的(上)
3分鐘純Java注解搭個管理系統(tǒng)
![3分鐘純<b class='flag-5'>Java</b><b class='flag-5'>注解</b>搭個管理系統(tǒng)](https://file1.elecfans.com/web2/M00/8E/2A/wKgaomTDNluAM7j-AABRVE1t9cc219.png)
評論