1、什么是注解
An annotation is a form of metadata, that can be added to Java source code. Classes, methods, variables, parameters and packages may be annotated. Annotations have no direct effect on the operation of the code they annotate.
翻譯:
注釋是元數(shù)據(jù)的一種形式,可以添加到Java源代碼中??梢宰⑨岊?lèi)、方法、變量、參數(shù)和包。注釋對(duì)它們注釋的代碼的操作沒(méi)有直接影響。
????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????--by 百度翻譯
個(gè)人理解注解就是一個(gè)標(biāo)記,可以標(biāo)記在類(lèi)、字段、方法等上面,如果我們對(duì)這個(gè)標(biāo)記不做任何處理那么其實(shí)它不起任何作用
2、元注解
元注解就是注解的注解,主要有四個(gè):@Target、@Retention、@Inherited、@Documented
比如:

其中Target表示注解的作用范圍取ElementType中的值

Retention定義了注解在哪個(gè)級(jí)別可用,有三個(gè)取值:
RetentionPolicy.SOURCE:僅在源碼期間有效,在編譯期間被丟棄
RetentionPolicy.CLASS:保留到class文件中,也就是說(shuō)編譯期間都是可以對(duì)其進(jìn)行處理的
RetentionPolicy.RUNTIME:保留到運(yùn)行時(shí),即可以用過(guò)反射調(diào)用

如上圖所示,定義了三個(gè)注解Test1、Test2、Test3其中Test3是SOURCE級(jí)別的所以我們?cè)诜淳幾g出來(lái)的dex文件中看不到Test3,Test1和Test2則可以看到。如果我們繼續(xù)編寫(xiě)代碼在運(yùn)行期間反射的話那么Test2是可以被反射到的,Test1則找不到。(代碼不好貼,可以自行驗(yàn)證)
Inherited表示注解是否可以被繼承,意思是如果A被注解@Test1注解,B繼承A,如果@Test1被Inherited標(biāo)記那么classB.getAnnotation可以獲取到Test1,如果@Test1沒(méi)有被Inherited標(biāo)記那么那么classB.getAnnotation就無(wú)法獲取到Test1。(代碼不好貼,可以自行驗(yàn)證)

如上圖代碼所示,Handler3繼承Handler2,Handler2被Test2注解,Test2被Inherited注解,然后運(yùn)行程序打印log:
annotation: @annotation.test.jyb.com.annotation.Test2(value=101),可以發(fā)現(xiàn)通過(guò)Handler3我們可以取到Test2注解
Documented表示注解是否會(huì)保留到文檔中
3、AbstractProcessor
AbstractProcessor只能在java library工程里面用,在Android library里面找不到這個(gè)類(lèi),定義編譯期間注解最核心的是要用到AbstractProcessor用于處理我們的注解,它有四個(gè)方法可以讓我們重寫(xiě)
?1、 public synchronized void init(ProcessingEnvironment processingEnv)
? 2、 public Set getSupportedAnnotationTypes()
? 3、 public SourceVersion getSupportedSourceVersion()
? 4、 public boolean process(Set annotations, RoundEnvironment roundEnv)
init方法主要是用于初始化和獲取相關(guān)工具類(lèi),Init:初始化工作,獲取工具類(lèi):
processingEnv.getElementUtils(): element操作相關(guān)工具類(lèi);
processingEnv.getTypeUtils():type操作相關(guān)工具類(lèi);
processingEnv.getMessager():打印日志;
processingEnv.getFiler(),生成java文件;
processingEnv.getOptions(), 獲取gradle中配置的參數(shù)
其中用的比較多的是打印日志和生成java文件
getSupportedAnnotationTypes:配置processor處理的注解類(lèi)。就是指定當(dāng)前process處理哪幾個(gè)注解(我們可能會(huì)定義多個(gè)注解,用多個(gè)process進(jìn)行處理,這個(gè)函數(shù)告訴當(dāng)前process處理哪幾個(gè)注解)
getSupportedSourceVersion: 設(shè)置支持的java版本,一般設(shè)置為SourceVersion.latestSupported()
process處理注解的方法,主要是獲取注解然后根據(jù)注解生成代碼
4、定義編譯期注解
下面用一個(gè)例子來(lái)說(shuō)民如何定義編譯期間注解,
第一步定義注解:

如上圖,我們定義了一個(gè)注解可以用在類(lèi)、方法、字段上面,CLASS級(jí)別(無(wú)法反編譯獲?。?,可以被繼承
第二步編寫(xiě)process方法
新建java library工程,編寫(xiě)我們的process類(lèi)繼承AbstractProcessor

這里我們的注解上也添加了一個(gè)@AutoService注解,這個(gè)AutoService作用是我們不用新建resources文件夾和配置文件。使用方法就是在當(dāng)前java library工程中添加Gradle依賴(implementation'com.google.auto.service:auto-service:1.0-rc4')然后在process類(lèi)上添加注解就行
設(shè)置Procerssor能夠處理的注解

重寫(xiě)process方法

上面process方法,首先通過(guò)roundEnv獲取被Test1標(biāo)記的類(lèi)或者方法或者字段,如果沒(méi)有找到那么退出
TypeSpec.Builder用于生成一個(gè)類(lèi)(使用javapoet庫(kù)幫助我們生成代碼)
第三部添加一個(gè)單例方法(也是使用javapoet庫(kù))
for循環(huán),element就是被注解標(biāo)記的元素,先判斷它是否是類(lèi)如果是那么添加成員函數(shù)
最后createFile生成代碼。



addInstanceMethod,addClassMember都是通過(guò)javapoet來(lái)生成方法和字段的,createFile生成java文件。最后編譯我們可以在
app\build\intermediates\classes\debug\annotation\test\jyb\com中查看,標(biāo)黑的這個(gè)是我們生成類(lèi)的時(shí)候設(shè)置的包名,所以它會(huì)隨著包名的變化而變化

4、定義運(yùn)行期間注解
定義運(yùn)行期間注解比較簡(jiǎn)單,就是在定義注解的時(shí)候?qū)?b>Retention設(shè)置為RunTime然后通過(guò)反射調(diào)用就行,比如我們?nèi)绻眠\(yùn)行期間注解實(shí)現(xiàn)ButterKnife的話需要以下步驟:
第一步定義注解:

第二部編寫(xiě)輔助代碼

第三步使用

然后在Activity的Oncreate方法中調(diào)用InjectBindHelper.inject(this)即可,因?yàn)榉瓷湫时容^低所以一般不會(huì)用這種方法來(lái)用findViewById,這里只是演示;
完整demo:https://github.com/yaozhukuang/component,這個(gè)demo是通過(guò)注解實(shí)現(xiàn)項(xiàng)目組件化方案,所以代碼跟文中的可能有不一樣