如果我们通过method.getAnnotation(xx)方法,去获取对应的注解。我们可以发现,注解都是一个动态生成的Proxy类。
示例一个注解:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Ann0tation {
String attr();
}
$Proxy126.通过执行getClass().getName()方法,得到对应的proxy类名为com.sun.proxy.$Proxy126.
这个类只有一个属性h。而h就是这样一个类:sun.reflect.annotation.AnnotationInvocationHandler.
这里就涉及java生成的Proxy类。通过 Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this)就可以生成一个动态的类。该类就是注解对应的类。
注解对应的各个方法,是在proxy类中生成的。类似这样:
public final String attr()
{
try
{
return (String)this.h.invoke(this, m3, null);
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
对应的注解的属性,其实是在Proxy类的唯一的属性h中的。
h的构造函数为:
private final Map<String, Object> memberValues;
AnnotationInvocationHandler(Class<? extends Annotation> paramClass, Map<String, Object> paramMap)
{
Class[] arrayOfClass = paramClass.getInterfaces();
if ((!paramClass.isAnnotation()) || (arrayOfClass.length != 1) || (arrayOfClass[0] != Annotation.class))
{
throw new AnnotationFormatError("Attempt to create proxy for a non-annotation type.");
}this.type = paramClass;
this.memberValues = paramMap;
}
因此,最终缓存的属性其实都是存储在memberValues的map中。我们看到attr方法最终是调用的是,(String)this.h.invoke(this, m3, null);方法。我们先看下Proxy类和h是如何关联起来的。
Proxy
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
内部方法走到
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
return cons.newInstance(new Object[]{h});
依旧是最终调用了Proxy的构造函数
protected Proxy(InvocationHandler h) {
Objects.requireNonNull(h);
this.h = h;
}
也就是Proxy的newProxyInstance最终是把InvocationHandler的实现类设置到Proxy类的h属性中了。
因为注解的h对应的就是sun.reflect.annotation.AnnotationInvocationHandler.。来看下该类的
String)this.h.invoke(this, m3, null);方法
主要是这么两句话:
public Object invoke(Object paramObject, Method paramMethod, Object[] paramArrayOfObject) {
String str = paramMethod.getName();
....
localObject = this.memberValues.get(str);
....
return localObject;
}
示例的Ann0tation 举例来说,调用attr的时候,methodName为attr。然后memberValues本质上就是一个map,key就是attr,value就是attr对应的值。这样就通过map得到了attr方法返回的值了。并返回数据。
总结:
注解因为可以调用对应的方法。例如示例中的attr方法等。注解的class通过javap反编译后,可以看到其实注解就是一个接口。
示例对应的是:
public
interface
me.kisimple.just4fun.Ann0tation
extends
java.lang.annotation.Annotation
但是我们的代码中又可以写成Ann0tation.attr方法。因此这里具体实现必然是该接口的一个实现。对jdk来说,就是采用
Proxy类生成一个代理类。代理类的注解方法的实现实际上都会调用Proxy的唯一的属性h的invoke方法,在h的invoke方法中,
其实就是从map中获取对应的方法名的value值。该value值就是在使用注解中写的例如attr="xx",对应的map中key->value为
attr->xx。在invoke方法中返回xx即可。
参考文章: