註解是一種附加信息/標識信息,若是沒有對應的處理器,那沒有任何做用.java
註解留存的時間節點:ide
註解做用的範圍:this
是否容許子類繼承該註解,注意,這不是說註解容許繼承,而是若是你用註解標註了一個父類,是否容許這個父類的子類也自動擁有這個註解.spa
註解是否應當被包含在 JavaDoc 文檔中代理
/** @deprecated This class is full of bugs. Use MyNewComponent instead. */
複製代碼
先看一個接口public interface Annotation
,在它的doc描述中有一句The common interface extended by all annotation types
,也就是全部註解都會繼承這個接口.那麼註解其實也是接口,它在運行時會有代理類,這也就解釋了,爲何定義註解使用的變量看起來會有點奇怪,竟然要定義個方法.code
@Target({ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Hello {
String helloKey() default "helloValue";
}
複製代碼
由於註解實際會有代理類,因此定義的屬性確實是方法,只不過方法名字和屬性名相同,屬性值由方法return. 這裏可能有人會問,爲何不直接定義變量,由於java用接口實現註解,接口沒法定義實例變量.cdn
其實後面就是代理的事情了,默認是JDK的動態代理.
能夠使用System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
來在當前工程中保存JDK生成的動態代理類.
看一下代理類對象
public final class $Proxy1 extends Proxy implements Hello {
// equals
private static Method m1;
// toString
private static Method m2;
// annotationType
private static Method m4;
// hashCode
private static Method m0;
// 註解定義的屬性對應的方法
private static Method m3;
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.learn.java.annotation.Hello").getMethod("annotationType");
m3 = Class.forName("com.learn.java.annotation.Hello").getMethod("helloKey");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
public $Proxy1(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 String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
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 int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String helloKey() throws {
try {
return (String)super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
}
複製代碼
能夠看出,Proxy類的實際邏輯都是交給InvocationHandler h的invoke方法處理的,
invoke(this,方法Method對象,方法參數列表),這是JDK代理的通用邏輯,代理後的實際邏輯在對應的handler. 對於註解,它的處理handler是AnnotationInvocationHandler
.
同時,咱們能夠看到代理類Proxy對四個方法是有特殊處理的,這四個方法的實際邏輯也在對應的handler.若是沒有這樣操做的話,其實代理類會使用Object的實現或者找不到.blog
AnnotationInvocationHandler的invoke方法.繼承
class AnnotationInvocationHandler implements InvocationHandler, Serializable {
private static final long serialVersionUID = 6182022883658399397L;
private final Class<? extends Annotation> type;
private final Map<String, Object> memberValues;
private transient volatile Method[] memberMethods = null;
// var1是Proxy$1代理類的this,var2是傳進來的方法對象,var3是方法參數
// 咱們只關注var2就好,別的沒用到
public Object invoke(Object var1, Method var2, Object[] var3) {
// 方法名
String var4 = var2.getName();
// 方法參數
Class[] var5 = var2.getParameterTypes();
// equals特殊處理
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;
// 這裏判斷方法名的hashcode
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:
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;
}
}
}
}
}
複製代碼
能夠看到,若是是4種特殊方法,那麼var7會被設成對應的值,直接調用invoker中複寫的方法. 若是是equals在上邊就直接判斷了.
若是不是,說明方法是註解自定義的方法,其實也就是註解的參數.
會從memberValues中取得,它是一個Map<String, Object>,會存儲當前註解的屬性名(方法名)爲Key,以及對應的值.
舉個例子
@Target({ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Hello {
String helloKey() default "helloValue";
}
複製代碼
那麼memberValues存放的值是