概述
上一篇已經(jīng)記載了關(guān)于Retrofit使用的相關(guān)知識(shí)卢厂,這一片主要記錄下Retrofit實(shí)現(xiàn)的相關(guān)原理沟沙。簡(jiǎn)要來說河劝,主要是運(yùn)用了注解,動(dòng)態(tài)代理等技術(shù)矛紫,這一篇先講下注解赎瞎。
注解
什么是注解
首先來了解下注解是什么。
注解是自Java1.5版本引入的颊咬;同時(shí)引入的還有反射务甥,注解的實(shí)現(xiàn)必須依賴反射,但是反射并不依賴注解喳篇。
注解是一種應(yīng)用于類敞临、方法、參數(shù)杭隙、變量哟绊、構(gòu)造器及包聲明中的特殊修飾字符。他是一種由JSR-175標(biāo)準(zhǔn)選擇用來描述元數(shù)據(jù)的一種工具痰憎。
舉個(gè)栗子:
@Override
public String toString() {
return "This is to String";
}
@Override告訴編譯器這個(gè)方法是重寫的父類的方法票髓,如果父類中不存在這個(gè)方法,那么編譯器就會(huì)報(bào)錯(cuò)铣耘,提示父類中沒有這個(gè)方法洽沟。但是如果toString寫成了toStrring,那么沒有使用@Override也是沒有問題的蜗细。但是運(yùn)行的結(jié)果就跟toString沒有什么關(guān)系了裆操。
為什么要引入注解
注解的常見作用有一下幾種:
- 生成文檔。 這是最常見的炉媒,也是java最早提供的注解踪区。比如:@see @param @return等等
- 跟蹤代碼依賴性,實(shí)現(xiàn)替代配置文件功能吊骤。這個(gè)就類似Retrofit中的注解的功能缎岗。
- 在編譯的時(shí)候進(jìn)行格式檢查。比如:@Override放在方法前白粉,如果你這個(gè)方法并不是覆蓋超類的方法就能檢查出來传泊。
原理
注解從本質(zhì)上來說其實(shí)是一種接口。注解本身是不會(huì)影響程序代碼的執(zhí)行鸭巴,無論注解怎么變化眷细,代碼都是一樣的執(zhí)行。Java語(yǔ)言解釋器在工作的時(shí)候會(huì)忽略這些注解鹃祖,因此在JVM中這些注解是“不起作用的”溪椎。但是Java可以通過反射的機(jī)制來訪問注解信息。相關(guān)的類根據(jù)注解中的信息再?zèng)Q定如何改變程序的元素或者改變他們的行為。
注解與接口又存在一些區(qū)別:
- 注解類型使用的關(guān)鍵字是@interface而不是interface池磁。(這個(gè)關(guān)鍵字聲明隱含了一個(gè)信息:他是繼承java.lang.annotation.Annotation接口奔害,而不是聲明了一個(gè)interface 楷兽。)
- 注解類型地熄、方法定義是獨(dú)特的、受限制的芯杀。(注解里沒有直接定義成員名端考,方法名就是表示注解在使用時(shí)用到的成員名,而方法的返回值表示成員名的類型)
注解與接口相似的地方:他們都可以定義常量揭厚,靜態(tài)成員類型却特。注解類型也可以像接口一樣被實(shí)現(xiàn)或者繼承。
自定義Annotation
在介紹如何自定義注解之前筛圆,先來了解下元注解裂明,這些元注解在自定義注解過程中用得到
元注解
java.lang.annotation提供了四種元注解,專門用來注解其他的注解太援。
- @Documented:一個(gè)簡(jiǎn)單的Annotations標(biāo)記注解闽晦,表示是否將注解信息添加在java文檔中。
- @Retention:表示在什么級(jí)別保存該注解信息提岔。
- @Target:表示注解用在什么地方仙蛉。
- @Inherited:表示允許子類繼承父類中的注解。
Retention定義了注解的生命周期碱蒙,有以下幾個(gè)枚舉選項(xiàng):
- RetentionPolicy.SOURCE:在編譯階段丟棄荠瘪。這些注解在編譯結(jié)束之后就不再有任何意義,所以他們不會(huì)寫入字節(jié)碼赛惩。 @Override哀墓,@SuppressWarnings都屬于這類注解。
- RetentionPolicy.CLASS:在類加載階段丟棄喷兼。在字節(jié)碼文件的處理中有用篮绰。注解默認(rèn)使用這種方式。
- RetentionPolicy.RUNTIME:始終不會(huì)丟棄褒搔,運(yùn)行時(shí)期也是保留該注解阶牍,因此可以使用反射機(jī)制來讀取該注解的信息。這也是我們自定義注解常用的方式星瘾。
Target 表示注解用在什么地方走孽。如果沒明確指出,默認(rèn)是可以放在任何地方的琳状。以下是一些可用的參數(shù)磕瓷。需要說明的是:屬性的注解是兼容的,如果你想給7個(gè)屬性都能添加注解,僅僅排除一個(gè)屬性困食,那么你需要在定義target包含的所有屬性边翁。
- ElementType.TYPE: 用于描述類。接口或enum聲明
- ElementType.FIELD:用于描述實(shí)例變量
- ElementType.METHOD:用于描述方法
- ElementType.PARAMETER:用于描述參數(shù)
- ElementType.CONSTRUCTOR:用于描述構(gòu)造函數(shù)
- ElementType.LOCAL_VARIABLE:用于描述局部變量
- ElementType.ANNOTATION_TYPE 用于注解另一個(gè)注解
- ElementType.PACKAGE 用于記錄java文件的package信息
Inherited 定義該注釋和子類的關(guān)系硕盹。
自定義注解
首先定義兩個(gè)自定義的注解:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ClassAnnotation {
String value() default "這是一個(gè)類的注解";
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MethodAnnotation {
String value() default "這是一個(gè)方法的注解";
String name();
}
定義一個(gè)運(yùn)用注解的普通類:
@ClassAnnotation
public class AnnotationUser {
@MethodAnnotation(name="test method")
public void testMethod(){
System.out.println("測(cè)試方法");
};
}
解釋注解:
Class iAnnotationUser = Class.forName("com.test.retrofit.annotation.AnnotationUser");
Method[] methods = iAnnotationUser.getMethods();
boolean isAP = iAnnotationUser.isAnnotationPresent(ClassAnnotation.class);
if(isAP){
ClassAnnotation classAnnotation = (ClassAnnotation)(iAnnotationUser.getAnnotation(ClassAnnotation.class));
System.out.println("類注解信息:"+classAnnotation.value());
}
for(Method m:methods){
if(m.isAnnotationPresent(MethodAnnotation.class)){
MethodAnnotation ma = m.getAnnotation(MethodAnnotation.class);
System.out.println("方法注解:"+ma.value()+" 給注解成員賦值:"+ma.name());
m.invoke(new AnnotationUser(), null);
}
}
運(yùn)行結(jié)果:
類注解信息:這是一個(gè)類的注解
方法注解:這是一個(gè)方法的注解 給注解成員賦值:test method
測(cè)試方法
以上就是注解的相關(guān)基本知識(shí)和運(yùn)用符匾。
另外關(guān)于注解的實(shí)現(xiàn),知乎上有個(gè)問答解釋得較好: http://www.zhihu.com/question/24401191