电子说
大部分情况下,我们的项目或者开源框架中都定义了大量的注解,而且都是@Retention(RetentionPolicy.RUNTIME)
运行时阶段,我们可以通过反射获取注解中的信息,所以整体遵循下面的一个范式。
下面我们重点使用下如何用反射来获取注解的信息。
@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") //注解方法参数
public String hello(@FullAnnoTest("methodParameter") String name)
throws @FullAnnoTest("Exception") Exception { // 注解抛出异常
//注解局部变量,现在运行时暂时无法获取(忽略)
@FullAnnoTest("result") String result;
result = "siting";
System.out.println(name);
return result;
}
public static void main(String[] args) throws Exception {
TestAnnoReflect
修饰TestAnnoReflect.class注解value: testAnnoReflect
修饰构造器的注解value: constructor
修饰继承父类的注解value: parent
修饰注解的注解AnnoTest-value: alvinAnno
修饰泛型参数T注解value: parameter
修饰普通字段域name注解value: name
修饰泛型字段T注解value: value
修饰泛型注解value: list
修饰通配符注解value: generic
修饰方法的注解value: method
修饰方法抛出错误的注解value: Exception
修饰方法参数注解value: methodParameter
修饰package注解value: package
hello
大家有没有想过注解的本质是什么?
我们先通过反编译查看注解生成的字节码,可以通过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();
}
所以注解相当于一个语法糖一样,可以方便我们使用,本质上它是继承自Annotation
的一个接口。
那大家有没有想过它的实现类在哪里?比如下面的代码,获取到注解,按照上面的解释,它是一个接口,那调用value()方法时,它具体调用的哪个实现类呢?我们并没有写实现类啊.....
答案当然就是动态代理生成的实现类。
AnnoTest annoTest = testTmp.annotationType().getAnnotation(AnnoTest.class);
System.out.println("修饰注解的注解AnnoTest-value: "+annoTest.value());
我们可以在启动参数添加如下命令可以查看生成的代理类:-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true
执行后,生成代理类如下,
代理大致的代码如下:
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()
方法,这里调用了super.h对象,也就是InvocationHandler
对象,而我们注解用的是AnnotationInvocationHandler
这个子类,我们在invoke方法中打个断点,就明白了~~
全部0条评论
快来发表一下你的评论吧 !