使用反射操作注解
大部分情況下,我們的項(xiàng)目或者開(kāi)源框架中都定義了大量的注解,而且都是@Retention(RetentionPolicy.RUNTIME)
運(yùn)行時(shí)階段,我們可以通過(guò)反射獲取注解中的信息,所以整體遵循下面的一個(gè)范式。
- 自定義注解
- 掃描注解
- 通過(guò)反射獲取注解的信息,執(zhí)行相應(yīng)的邏輯。
下面我們重點(diǎn)使用下如何用反射來(lái)獲取注解的信息。
- 定義target是注解的注解
@Inherited
@Retention( value = RetentionPolicy.RUNTIME)
@Target(value = {ElementType.ANNOTATION_TYPE})
public @interface AnnoTest {
String value() default "anno";
}
- 定義一個(gè)幾乎全量信息的注解
@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";
}
- 定義測(cè)試類和反射代碼
@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)在運(yùn)行時(shí)暫時(shí)無(wú)法獲取(忽略)
@FullAnnoTest("result") String result;
result = "siting";
System.out.println(name);
return result;
}
public static void main(String[] args) throws Exception {
TestAnnoReflect
- 查看對(duì)應(yīng)的執(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
修飾方法拋出錯(cuò)誤的注解value: Exception
修飾方法參數(shù)注解value: methodParameter
修飾package注解value: package
hello
注解的本質(zhì)和底層實(shí)現(xiàn)
大家有沒(méi)有想過(guò)注解的本質(zhì)是什么?
我們先通過(guò)反編譯查看注解生成的字節(jié)碼,可以通過(guò)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();
}
所以注解相當(dāng)于一個(gè)語(yǔ)法糖一樣,可以方便我們使用,本質(zhì)上它是繼承自Annotation
的一個(gè)接口。
那大家有沒(méi)有想過(guò)它的實(shí)現(xiàn)類在哪里?比如下面的代碼,獲取到注解,按照上面的解釋,它是一個(gè)接口,那調(diào)用value()方法時(shí),它具體調(diào)用的哪個(gè)實(shí)現(xiàn)類呢?我們并沒(méi)有寫(xiě)實(shí)現(xiàn)類啊.....
答案當(dāng)然就是動(dòng)態(tài)代理生成的實(shí)現(xiàn)類。
AnnoTest annoTest = testTmp.annotationType().getAnnotation(AnnoTest.class);
System.out.println("修飾注解的注解AnnoTest-value: "+annoTest.value());
我們可以在啟動(dòng)參數(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對(duì)象,也就是InvocationHandler
對(duì)象,而我們注解用的是AnnotationInvocationHandler
這個(gè)子類,我們?cè)趇nvoke方法中打個(gè)斷點(diǎn),就明白了~~
-
JAVA
+關(guān)注
關(guān)注
20文章
2984瀏覽量
106786 -
JDK
+關(guān)注
關(guān)注
0文章
82瀏覽量
16811 -
spring框架
+關(guān)注
關(guān)注
0文章
7瀏覽量
2090
發(fā)布評(píng)論請(qǐng)先 登錄
Java中注解的作用

如何通過(guò)注解來(lái)優(yōu)化我們的Java代碼
詳細(xì)介紹了Java泛型、注解、并發(fā)編程
HarmonyOS注解的使用方法分享
斬波型運(yùn)放減少噪聲 怎么做到的?

分析java注解基本概念
Spring Boot常用注解與使用方式
注解定義Bean及開(kāi)發(fā)
JAVA中注解是怎么做到的(上)
3分鐘純Java注解搭個(gè)管理系統(tǒng)

怎么做到EMC設(shè)計(jì)與產(chǎn)品設(shè)計(jì)同步?(上)

評(píng)論