本文目錄:
- 一. 注解概念和介紹
- 二. 注解的語法
- 三. 基本注解——五大元注解
- 四. AnnotatedElement 接口
- 五. AbstractProcessor (注解處理器)
- 六. javapoet(第三方庫召锈,生成.java源文件)
- 七. APT技術(shù)
- 八. demo實戰(zhàn)
一.注解概念和介紹
- 注解(annotation)也叫元數(shù)據(jù)瓣蛀。JDK1.5版本后引入的特性喜每,它可以聲明在包,類钞澳,字段,方法,局部變量撑教,方法參數(shù)等的前面,用來對這些元素進行說明和注釋偷遗。
二.注解的語法
- 注解通過@interface關(guān)鍵字來定義墩瞳。
三.基本注解——五大元注解
@Retention, @Documented, @Target, @Inherited, @Repeatable
元注解是指可以注解到注解上的注解,即使用了@Target(ElementType.ANNOTATION_TYPE)氏豌。
五大元注解 | 介紹 |
---|---|
@Retention | 指注解保留的時長 |
@Documented | 跟文檔相關(guān)喉酌,其作用是能夠?qū)⒆⒔庵械脑匕絡(luò)avadoc中去 |
@Target | 指定注解可作用的目標(biāo) |
@Inherited | 父類被(Inherited修飾的注解)注解修飾,它的子類如果沒有任何注解修飾泵喘,就會繼承父類的這個注解(看下面例子更好理解) |
@Repeatable | 表明標(biāo)記的注解可以多次應(yīng)用于相同的聲明或類型 |
注意:@Repeatable JDK1.8以上才有泪电,Android系統(tǒng)7.0(對應(yīng)SDK24)以上才有
-
3.1@Retention:中文保留的意思,指注解保留的時長纪铺。
三種取值如下:
@Retention三種取值 | 介紹 | 用途 |
---|---|---|
RetentionPolicy.SOURCE | 注解只在源碼階段保留相速,批注將被編譯器丟棄。 | 主要用于提示開發(fā)者 |
RetentionPolicy.CLASS | 注解保留到編譯期(注解將由編譯器記錄在類文件中)鲜锚,但在虛擬機VM運行時不需要保留他們突诬。這是默認(rèn)的。 | 主要用于自動生成代碼 |
RetentionPolicy.RUNTIME | 注解會保留到程序運行時(注解由編譯器記錄在類文件中烹棉,并在運行時由虛擬機VM保留)攒霹,因此可以通過反射方式讀取它們。 | 主要用于自動注入 |
@Retention 源碼:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
RetentionPolicy value();
}
@RetentionPolicy 源碼
public enum RetentionPolicy {
SOURCE,
CLASS,
RUNTIME
}
-
3.2@ Documented: 中文文獻的意思浆洗,可看出跟文檔相關(guān)催束,其作用是能夠?qū)⒆⒔庵械脑匕絡(luò)avadoc中去。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}
-
3.3@Target: 指定注解可作用的目標(biāo)
10種取值如下:
@ Target十種取值 | 作用 |
---|---|
1. ElementType.TYPE | 可作用在類伏社、接口抠刺、枚舉上 |
2. ElementType.FIELD | 可作用在屬性上 |
3. ElementType.METHOD | 可作用在方法上 |
4. ElementType.PARAMETER | 可作用在方法參數(shù)上 |
5. ElementType.CONSTRUCTOR | 可作用在構(gòu)造方法上 |
6. ElementType.LOCAL_VARIABLE | 可作用在局部變量上,例如方法中定義的變量 |
7.ElementType.ANNOTATION_TYPE | 可以作用在注解上 |
8.ElementType.PACKAGE | 可作用在包上 |
9. ElementType.TYPE_PARAMETER | JDK1.8才有 |
10. ElementType. TYPE_USE | JDK1.8才有 |
@Target源碼
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
ElementType[] value();
}
上面看@Target源碼可知其取值為一個數(shù)組摘昌,舉兩個例子速妖,一個取值和兩個取值以上:@Target(ElementType.ANNOTATION_TYPE),@Target({ElementType.ANNOTATION_TYPE聪黎,ElementType.PACKAGE})
ElementType 源碼
public enum ElementType {
TYPE,
FIELD,
METHOD,
PARAMETER,
CONSTRUCTOR,
LOCAL_VARIABLE,
ANNOTATION_TYPE,
PACKAGE,
TYPE_PARAMETER,
TYPE_USE
}
-
3.4@Inherited: 繼承的意思罕容,但并不是注解本身可被繼承,而是指一個父類SuperClass被該類注解修飾稿饰,那么它的子類SubClass如果沒有任何注解修飾锦秒,就會繼承父類的這個注解。
例子:
@Inherited
@Target(ElementType.Type)
@Retention(RetentionPolicy.RUNTIME)
public @interface Money{}
@Money
public class Father{}
public class Son extends Father {}
解釋:注解Money被@Inherited修飾喉镰,F(xiàn)ather被Money修飾旅择,Son 繼承Father(Son 上又無其他注解),那么Son 就會擁有Money這個注解侣姆。
-
3.5@ Repeatable生真,這個詞是可重復(fù)的意思沉噩,它是java1.8引入的,算一個新特性柱蟀。
什么樣的注解可以多次應(yīng)用來呢川蒙,通常是注解可以取多個值
例子:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @Interface Teachers{
Teacher[] value();
}
@Repeatable(Teachers.class)
public @Interface Teacher{
String role() default ""
}
@Teacher(role="Chinese")
@Teacher(role="Math")
@Teacher(role="English")
public class Me {}
public static void main(String[] args) {
if(Me .class.isAnnotationPresent(Teachers.class)) {
Teachers teaches=Man.class.getAnnotation(Teachers.class);
for(Teacher teacher : teachers.value()){
System.out.println(teacher.role());
}
}
例子解析:@Teacher被@Repeatable修飾,所以Teacher可以多次作用在同一個對象Me上产弹,而Repeatable接收一個參數(shù)派歌,這個參數(shù)是個容器注解,用來存放多個@Person痰哨。
四. AnnotatedElement 接口
- 該接口表示此虛擬機VM中當(dāng)前正在運行的程序的帶注釋元素胶果。
- 所有實現(xiàn)了這個接口的“元素”都是可以“被注解的元素”。
- 使用這個接口中聲明的方法可以讀冉锔(通過Java的反射機制)“被注解元素”的注解早抠。 即該接口允許以反射方式讀取注解。
- 該接口中方法返回的所有注釋都是不可變的和可序列化的撬讽。 調(diào)用方可以修改此接口的方法返回的數(shù)組蕊连,而不會影響返回給其他調(diào)用方的數(shù)組。
1.AnnotatedElement 方法:
AnnotatedElement 方法 | 介紹 |
---|---|
default boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) | 如果指定類型的注解出現(xiàn)在當(dāng)前元素上游昼,則返回true甘苍,否則將返回false。這種方法主要是為了方便地訪問一些已知的注解烘豌。 |
<T extends Annotation> T getAnnotation(Class<T> annotationClass) | 如果在當(dāng)前元素上存在參數(shù)所指定類型(annotationClass)的注解载庭,則返回對應(yīng)的注解,否則將返回null廊佩。 |
Annotation[] getAnnotations() | 返回在這個元素上的所有注解囚聚。如果該元素沒有注釋,則返回值是長度為0的數(shù)組标锄。 |
default <T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass) | 返回與該元素相關(guān)聯(lián)的注解顽铸。如果沒有與此元素相關(guān)聯(lián)的注解,則返回值是長度為0的數(shù)組料皇。這個方法與getAnnotation(Class)的區(qū)別在于谓松,該方法檢測其參數(shù)是否為可重復(fù)的注解類型,如果是践剂,則嘗試通過“l(fā)ooking through”容器注解來查找該類型的一個或多個注解毒返。 |
default <T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass) | 如果參數(shù)中所指定類型的注解是直接存在于當(dāng)前元素上的,則返回對應(yīng)的注解舷手,否則將返回null。這個方法忽略了繼承的注解劲绪。(如果沒有直接在此元素上顯示注釋男窟,則返回null盆赤。) |
default <T extends Annotation> T[] getDeclaredAnnotationsByType(Class<T> annotationClass) | 如果參數(shù)中所指定類型的注解是直接存在或間接存在于當(dāng)前元素上的,則返回對應(yīng)的注解歉眷。這種方法忽略了繼承的注釋牺六。如果沒有直接或間接地存在于此元素上的指定注解,則返回值是長度為0的數(shù)組汗捡。這個方法和getDeclaredAnnotation(Class)的區(qū)別在于淑际,這個方法檢測它的參數(shù)是否為可重復(fù)的注釋類型(JLS 9.6),如果是扇住,則嘗試通過“l(fā)ooking through”容器注解來查找該類型的一個或多個注解春缕。 |
Annotation[] getDeclaredAnnotations() | 返回直接出現(xiàn)在這個元素上的注解。這種方法忽略了繼承的注解艘蹋。如果在此元素上沒有直接存在的注解锄贼,則返回值是長度為0的數(shù)組。 |
2.AnnotatedElement 的實現(xiàn)類:
AnnotatedElement的實現(xiàn)類 | 介紹 | 備注 |
---|---|---|
Class | 類女阀,天天打交道的 | \ |
Package | 包 | \ |
Parameter | 參數(shù)宅荤,主要指方法或函數(shù)的參數(shù),其實是這些參數(shù)的類型 | JDK1.8才有 |
AccessibleObject | 是Field浸策、Method 和 Constructor 對象的基類冯键。所以可訪問對象,如:方法庸汗、構(gòu)造器惫确、屬性等 | \ |
Field | 屬性,類中屬性的類型 | 繼承于AccessibleObject |
Executable | 可執(zhí)行的夫晌,如構(gòu)造器和方法 | 繼承于AccessibleObject雕薪;JDK1.8才有 |
Constructor | 構(gòu)造器 | 繼承于Executable |
Method | 方法 | 繼承于Executable |
五. AbstractProcessor (注解處理器)
-
5.1AbstractProcessor介紹
注解處理器一共有七個方法,我們一般重寫四個方法
public class MyProcessor extends AbstractProcessor {
@Override
public synchronized void init(ProcessingEnvironment env){ }
@Override
public boolean process(Set<? extends TypeElement> annoations, RoundEnvironment env) { }
@Override
public Set<String> getSupportedAnnotationTypes() { }
@Override
public SourceVersion getSupportedSourceVersion() { }
}
5.1.1 init(ProcessingEnvironment env):
每一個注解處理器類都必須有一個空的構(gòu)造函數(shù)晓淀。然而所袁,這里有一個特殊的init()方法,它會被注解處理工具調(diào)用凶掰,并輸入ProcessingEnviroment參數(shù)燥爷。ProcessingEnviroment提供很多有用的工具類Elements, Types和Filer。
5.1.2 process(Set<? extends TypeElement> annotations, RoundEnvironment env):
這相當(dāng)于每個處理器的主函數(shù)main()懦窘。你在這里寫你的掃描前翎、評估和處理注解的代碼,以及生成Java文件畅涂。輸入?yún)?shù)RoundEnviroment港华,可以讓你查詢出包含特定注解的被注解元素。
5.1.3 getSupportedAnnotationTypes():
這里我們必須指定午衰,這個注解處理器是注冊給哪個注解的立宜。它的返回值是一個字符串的集合冒萄,包含本處理器想要處理的注解類型的合法全稱。換句話說橙数,我們在這里定義我們的注解處理器注冊到哪些注解上尊流。
5.1.4 getSupportedSourceVersion():
用來指定使用的Java版本。通常這里返回SourceVersion.latestSupported()灯帮。
-
5.2 AbstractProcessor使用(Android需新建Java Library)
5.2.1 寫一個注解
5.2.2 寫一個類繼承于AbstractProcessor崖技,然后重寫上述四個方法
5.2.3 添加SPI配置文件(手動或者使用google提供的auto-service庫)
手動配置:在該Java Library下的main目錄下創(chuàng)建resources/META-INF/services目錄,然后在 META-INF/services 目錄文件夾下創(chuàng)建javax.annotation.processing.Processor 文件钟哥;在 javax.annotation.processing.Processor 文件寫入注解處理器的全稱迎献,包括包路徑;包名+自定義處理器文件名)
自定義AbstractProcessor是APT技術(shù)的頂梁柱瞪醋,這里介紹了基本的知識忿晕,更多的請看相關(guān)的APT知識。
六. javapoet(square公司出品的第三方庫银受,生成java文件)
-
6.1 javapoet介紹:
JavaPoet是用于生成.java源文件的Java API践盼。由square公司出品的第三方庫。
在執(zhí)行諸如注解處理或與元數(shù)據(jù)文件(例如宾巍,數(shù)據(jù)庫模式咕幻,協(xié)議格式)交互之類的操作時,源文件的生成非常有用顶霞。 通過生成代碼肄程,無需編寫樣板文件,同時還保留了元數(shù)據(jù)的唯一真實來源选浑。
-
6.2 javapoet使用(Android需新建Java Library)
javapoet在AndroidStudio中不能直接使用蓝厌,需要新建一個Java Library徽千。Java Library中依賴compile 'com.squareup:javapoet:1.12.1'
下面例子MyJavapoet是寫在Java Library的蟋滴,運行MyJavapoet本缠,就可在目錄下看到生成的名為HelloWorld的Java文件了
public class MyJavapoet {
public static void main(String[] args) {
generateJava();
}
private static void generateJava() {
//1. 生成一個字段
FieldSpec fieldSpec = FieldSpec.builder(String.class, "var", Modifier.PUBLIC).build();
//2. 生成一個方法
MethodSpec main = MethodSpec.methodBuilder("main")//設(shè)置方法名稱
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)//設(shè)置方法名稱
.returns(void.class)//添加返回值
.addParameter(String[].class, "args")//添加參數(shù)
.addStatement("$T.out.println($S)", System.class, "Hello, JavaPoet!")//添加代碼語句 (結(jié)束語句的分號不需要, 注意與CodeBlock的區(qū)別)
.build();
//3. 生成類型(enum/class/annotation/interface)
TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addMethod(main)
.build();
//4. 構(gòu)建Java源文件
JavaFile javaFile = JavaFile.builder("annotation.hsj.pri", helloWorld)
.build();
//5.生成到當(dāng)前module的源文件目錄下
try {
javaFile.writeTo(System.out);
//輸出到和用例程序相同的源碼目錄下,targetDirectory后面會自動添加以上述包名為文件名的目錄
String targetDirectory = "annotationlib/src/main/java/";
File dir = new File(targetDirectory);
if (!dir.exists()) dir.mkdirs();
javaFile.writeTo(dir); //JavaFile.write(), 參數(shù)為源碼生成目錄(源碼的classpath目錄)
} catch (IOException e) {
e.printStackTrace();
}
}
}
javapoet的詳細信息請見javapoet的GitHub:https://github.com/square/javapoet
七. APT技術(shù)
APT技術(shù)主要是通過編譯期解析注解颗味,并且生成java代碼的一種技術(shù),熟練掌握了該技術(shù)芬膝,對于ButterKnife慧起、Dagger惩淳、EventBus里面的注解就非常容易理解了疹吃。
其三大基礎(chǔ)為:
- 一個注解(@Retention(RetentionPolicy.CLASS))蹦疑;
- 一個自定義的AbstractProcessor(注解解析器);
- 一個生成Java代碼的Javapoet技術(shù)萨驶。
這里限于篇幅歉摧,但又在自定義注解里非常重要的一個綜合知識面,建議大家先看完 八實戰(zhàn)demo 最后再來看APT技術(shù)。這里我就不寫了叁温,給大家推薦一篇專門講APT的文章:http://www.reibang.com/p/7af58e8e3e18
八. 實戰(zhàn)demo
8.1 按作用域上分(幾種常見的)
8.1.1 作用在屬性上(@Target(ElementType.FIELD))豆挽,如成員變量
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface MyFieldAnnotation { @IdRes int id() default -1; } --------------------------------------------------------------------------------------------- public class FieldActivity extends AppCompatActivity { @FieldAnnotation(id = R.id.tv) TextView textView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_field); field(); } private void field() { Field[] fields = this.getClass().getDeclaredFields(); //通過該方法設(shè)置所有的字段都可訪問,否則即使是反射券盅,也不能訪問private修飾的字段 Field.setAccessible(fields, true); for (Field field : fields) { if (field.isAnnotationPresent(FieldAnnotation.class)) { MyFieldAnnotation fieldAnnotation = field.getAnnotation(MyFieldAnnotation.class); int id = fieldAnnotation.id(); if (id == -1) continue; View view = this.findViewById(id); Class aClass = field.getType(); try { field.set(this, aClass.cast(view)); } catch (IllegalAccessException e) { e.printStackTrace(); } } } textView.setText("提莫隊長"); } }
例子分析:
- 作用在Field上即@Target(ElementType.FIELD)
- 這里是實現(xiàn)一個跟ButterKnife類似的功能,即自動注入功能膛檀,無需手動調(diào)用findViewById锰镀。
第一步:定義一個注解。我們這里定義了一個運行時注解咖刃,其取值id我們作為findVIewById的id值
第二步:定義注解解析工具泳炉。例子里是通過field方法代替解析工具的。通過Field[]獲取Activity的field成員變量嚎杨。然后 AccessibleObject.setAccessible()設(shè)置所有字段都可以訪問花鹅,這里AccessibleObject也可以改為Field,AccessibleObject是Field的父類枫浙。然后循環(huán)遍歷Field[]得到每個Field刨肃,判斷指定的注解MyInjectViewAnnotation是否在當(dāng)前Field元素上,如果在箩帚,則取出當(dāng)前Field元素上的MyInjectViewAnnotation注解真友,然后得到注解上的id值,然后就可以進行findViewById操作了紧帕。
第三步:使用注解盔然。例子里申明一個Field成員變量TextView,將注解作用于 TextView 上是嗜。
8.1.2 作用在方法上(@Target(ElementType.METHOD))
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface MyMethodAnnotation { String name() default ""; String sex() default ""; int age() default 0; } ----------------------------------------------------------------------------------------------------------------- public class MethodActivity extends AppCompatActivity { private TextView textView; private StringBuffer stringBuffer = new StringBuffer(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_method); textView = findViewById(R.id.tv_method); method(); } @MethodAnnotation(name = "德瑪", sex = "男", age = 18) public void method() { Method[] methods = this.getClass().getMethods(); AccessibleObject.setAccessible(methods, true); for (Method method : methods) { if (method.isAnnotationPresent(MethodAnnotation.class)) { MyMethodAnnotation methodAnnotation = method.getAnnotation(MyMethodAnnotation.class); stringBuffer.append("姓名:" + methodAnnotation.name() + methodAnnotation.age() + " 性別:" + " 年齡:" + methodAnnotation.sex()); Log.d("MethodActivity-->", "姓名:" + methodAnnotation.name() + " 年齡:" + methodAnnotation.age() + " 性別:" + methodAnnotation.sex()); } } textView.setText(stringBuffer); } }
疑問愈案?這里注解所作用的方法必須為public,即使設(shè)置AccessibleObject.setAccessible(methods, true);還是無法訪問private修飾的方法鹅搪,這里我不太明白站绪,有小伙伴知道的可以教教我哦。
在8.1.3 作用在方法參數(shù)上(@Target(ElementType.PARAMETER))
@Target(ElementType.PARAMETER) @Retention(RetentionPolicy.SOURCE) @StringDef({ParameterAnnotationStringA.PRIMARY_SCHOOL, ParameterAnnotationStringA.JUNIOR_SCHOOL, >ParameterAnnotationStringA.SENIOR_SCHOOL}) public @interface ParameterAnnotationStringA { String PRIMARY_SCHOOL = "小學(xué)"; String JUNIOR_SCHOOL = "初中"; String SENIOR_SCHOOL = "高中"; } --------------------------------------------------------------------------------------------------------------- @Target(ElementType.PARAMETER) @Retention(RetentionPolicy.SOURCE) @StringDef({"小學(xué)","初中","高中"}) public @interface ParameterAnnotationStringB { } --------------------------------------------------------------------------------------------------------------- @Target(ElementType.PARAMETER) @Retention(RetentionPolicy.SOURCE) @IntRange(from = 5, to = 20) public @interface ParameterAnnotationIntRange { } --------------------------------------------------------------------------------------------------------------- public class ParameterActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_parameter); parameterStringA("小學(xué)");//報錯:即使PRIMARY_SCHOOL也是小學(xué)涩嚣,但這里必須使用PRIMARY_SCHOOL和下條一樣 parameterStringA(ParameterAnnotationStringA.PRIMARY_SCHOOL); parameterStringB("小學(xué)"); parameterStringB("小學(xué)生");//報錯:因為ParameterAnnotationStringB沒有小學(xué)生 parameterStringB(ParameterAnnotationStringA.PRIMARY_SCHOOL); parameterIntRange(15); parameterIntRange(30);//報錯:因為ParameterAnnotationIntRange的IntRange是從5到20 } private void parameterStringA(@ParameterAnnotationStringA String schoolName) { Log.d("ParameterActivity-->", "parameterStringA():" + "schoolName " + schoolName); } private void parameterStringB(@ParameterAnnotationStringB String schoolName) { Log.d("ParameterActivity-->", "parameterStringB():" + "schoolName " + schoolName); } private void parameterIntRange(@ParameterAnnotationIntRange int age) { Log.d("ParameterActivity-->", "parameterIntRange():" + "age " + age); } }
例子解析:
- 打印結(jié)果依次為:小學(xué)崇众;小學(xué);小學(xué)航厚;小學(xué)生顷歌;小學(xué);15幔睬;30 眯漩;也就是這里寫代碼代碼的時候會提示警告信息,但編譯和運行不會出現(xiàn)任何錯誤。
- 作用在方法參數(shù)上即@Target(ElementType.PARAMETER)赦抖。案例中有寫@StringDef舱卡,@IntRange,這是對方法參數(shù)進行約束队萤。
- 如果注解中有寫了變量并賦值轮锥,拿ParameterAnnotationStringA注解舉例,那么就參數(shù)必須寫StringDef里的ParameterAnnotationStringA.PRIMARY_SCHOOL等要尔,而不能寫ParameterAnnotationStringA.PRIMARY_SCHOOL的值“小學(xué)”舍杜。
- 但是反過來,卻可以使用ParameterAnnotationStringA.PRIMARY_SCHOOL赵辕,如例子里的parameterStringA(ParameterAnnotationStringA.PRIMARY_SCHOOL);
- 對第三點和第四點進行總結(jié)既绩,即可以使用“小學(xué)”的一定可以使用ParameterAnnotationStringA.PRIMARY_SCHOOL,但可以使用ParameterAnnotationStringA.PRIMARY_SCHOOL的不一定可以使用“小學(xué)”还惠,即變量名稱大于變量的值饲握。這個跑一下demo更直觀更容易理解。
- 作用在方法參數(shù)上蚕键,這個可以常用于對參數(shù)進行約束救欧,比如開發(fā)中會常用到生產(chǎn)環(huán)境地址,測試環(huán)境地址以及其他地址嚎幸,為了開發(fā)者更統(tǒng)一規(guī)范避免地址錯誤颜矿,就可以對這些地址進行注解來進行約束。
8.2 按注解保留時長分(源碼期嫉晶,編譯期骑疆,運行期)
8.2.1 源碼期@Retention(RetentionPolicy.SOURCE)
demo和8.1.3一樣
8.2.2 編譯期@Retention(RetentionPolicy.CLASS)
編譯期很重要的一個是APT技術(shù),具體請詳見上面 七.APT技術(shù)
8.2.3 運行期@Retention(RetentionPolicy.RUNTIME)
demo和8.1.1一樣
運行時注解主要通過反射進行解析替废,代碼運行過程中箍铭,通過反射我們可以知道哪些屬性、方法使用了該注解椎镣,并且可以獲取注解中的參數(shù)诈火,做一些我們想做的事情
參考文章
http://www.reibang.com/p/7454a933dcaf
https://www.race604.com/annotation-processing/
https://tool.oschina.net/apidocs/apidoc?api=jdk-zh