public Object invoke(Object var1, Method var2, Object[] var3) {
//获取当前执行的方法名称
String var4 = var2.getName();
Class[] var5 = var2.getParameterTypes();
if (var4.equals("equals") && var5.length == 1 && var5[0] == Object.class) {
return this.equalsImpl(var3[0]);
} else if (var5.length != 0) {
throw new AssertionError("Too many parameters for an annotation method");
} else {
byte var7 = -1;
switch(var4.hashCode()) {
case -1776922004:
if (var4.equals("toString")) {
var7 = 0;
}
break;
case 147696667:
if (var4.equals("hashCode")) {
var7 = 1;
}
break;
case 1444986633:
if (var4.equals("annotationType")) {
var7 = 2;
}
}
switch(var7) {
case 0:
return this.toStringImpl();
case 1:
return this.hashCodeImpl();
case 2:
return this.type;
default:
//利用方法名称从memberValues获取成员属性的赋值
Object var6 = this.memberValues.get(var4);
if (var6 == null) {
throw new IncompleteAnnotationException(this.type, var4);
} else if (var6 instanceof ExceptionProxy) {
throw ((ExceptionProxy)var6).generateException();
} else {
//这一步就是注解成员属性返回值获取的实际逻辑
//需要判断是否数据,如果是数据需要克隆一个数组
//不是数组直接返回
if (var6.getClass().isArray() && Array.getLength(var6) != 0) {
var6 = this.cloneArray(var6);
}
return var6;
}
}
}
}
//忽略其他方法
这里需要重点注意一下的是:AnnotationInvocationHandler的成员变量Map<String, Object> memberValues存放着注解的成员属性的名称和值的映射,注解成员属性的名称实际上就对应着接口中抽象方法的名称,例如上面我们定义的@Counter注解生成代理类后,它的AnnotationInvocationHandler实例中的memberValues属性存放着键值对count=1。
既然知道了注解底层使用了JDK原生的Proxy,那么我们可以直接输出代理类到指定目录去分析代理类的源码,有两种方式可以输出Proxy类的源码:
1、通过Java系统属性设置System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");。
2、通过-D参数指定,其实跟1差不多,参数是:-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true。
这里使用方式1,修改一下上面用到的main方法: