前言
在前面寫的EventBus的源碼解析的文章中提到了編譯時(shí)注解的概念藐吮,在編譯期EventBusAnnotationProcessor會(huì)生成一個(gè)MyEventIndex的java類文件,并把帶有@subscribe的方法信息保存在該方法中,并在注冊(cè)的時(shí)候獲取訂閱方法信息橘忱,那么編譯器注解是如何工作的恃鞋?下面就給大家講講究竟這是個(gè)什么東西吧宪赶。
什么是注解告抄?
從JDK5開始,Java增加了注解愤估,注解是代理里特殊的標(biāo)記帮辟,這些標(biāo)記可以在編譯、類加載玩焰、運(yùn)行時(shí)被讀取织阅,并執(zhí)行相應(yīng)的處理。通過使用注解震捣,開發(fā)人員可以在不改變?cè)羞壿嫷那闆r下荔棉,在源文件中嵌入一些補(bǔ)充信息。代碼分析工具蒿赢,開發(fā)工具和部署工具可以通過這些補(bǔ)充信息進(jìn)行驗(yàn)證润樱,處理或者進(jìn)行部署。
注解的分類
(1)標(biāo)注注解
- @Overrride:對(duì)覆蓋超類中的方法進(jìn)行標(biāo)記羡棵,如果被標(biāo)記的方 法并沒有實(shí)際覆蓋超類中的方法壹若,則編譯器會(huì)發(fā)出警告。
- @Deprecate:提示開發(fā)者該方法已經(jīng)廢棄皂冰,不推薦使用店展。
- @SupperWarnings:選擇性地取消特定代碼中的警告。
(2)元注解
用來標(biāo)注其它注解而創(chuàng)建的新注解秃流,元注解的類型有以下幾種:
- @Target:注解所修飾的對(duì)象范圍
- @Inherited:表示注解可以被繼承
- @Documented:表示這個(gè)注解應(yīng)該被JavaDoc工具記錄
- @Rentation:用來聲明注解的保留策略
- @Repeable:JDK8新增赂蕴,允許一個(gè)注解在同一聲明類型(類,屬性或方法)上多次使用舶胀。
其中@Target注解的取值是一個(gè)ElementType類型的枚舉概说,其中有一下幾種取值,對(duì)應(yīng)不同的對(duì)象范圍嚣伐。
- ElementType.TYPE:能修飾類糖赔,接口或者枚舉類型。
- ElementType.FIELD:能修飾成員變量
- ElementType. METHOD:能修飾方法
- ElementType. PARAMETER:能修飾參數(shù)
- ElementType. CONSTRUCTOR:能修飾構(gòu)造方法
- ElementType.LOCAL_VARIABLE:能修飾局部變量
- ElementType. ANNOTATION_TYPE:能修飾注解
- ElementType. PACKAGE:能修飾包
- ElementType.TYPE_PARAMETER:能修飾參數(shù)聲明
- ElementType.TYPE_USE:使用類型
其中@Retention注解有3種類型轩端,分別表示不同級(jí)別的保留策略放典。
RetentionPolicy.SOURCE:源碼級(jí)注解,注解信息只會(huì)保留在Java源碼中,源碼在編譯后奋构,注解信息將會(huì)被丟棄骨田,不會(huì)保留在.class文件中。
RetentionPolicy.CLASS:編譯時(shí)注解声怔。注解信息會(huì)保留在.java源碼以及.class中。當(dāng)運(yùn)行java程序時(shí)舱呻,JVM會(huì)丟棄該注解信息醋火,不會(huì)保留在JVM中,許多注解類框架箱吕,如EventBus芥驳,ButterKnife他們都是使用編譯時(shí)注解信息開發(fā)的,針對(duì)編譯時(shí)注解我們采用AbstractProcessor來處理注解信息茬高。
RetentionPolicy.RUNTIME:運(yùn)行注解兆旬。當(dāng)運(yùn)行Java程序時(shí),JVM也會(huì)保留該注解信息怎栽,可以通過反射獲取該注解信息丽猬。
AnnotationProcessor的使用
(1)新建annotations庫
- 新建一個(gè)Java Lib庫annotations,其gradle配置文件如下:
apply plugin: 'java'
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
}
sourceCompatibility = "1.7"
targetCompatibility = "1.7"
- 定義一個(gè)注解BindView注解熏瞄,其源碼如下:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.CLASS)
public @interface BindView {
int value() default 0;
}
(2)新建processor 庫
- 新建一個(gè)processor java lib庫脚祟,并引用上面定義的annotations庫
apply plugin: 'java'
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
compile project(path: ':annotations')
}
sourceCompatibility = "1.7"
targetCompatibility = "1.7"
- 定義一個(gè)ButterKnifeProcessor類繼承AbstractProcessor類
package com.mtime.processor;
import com.mtime.annotations.BindView;
import java.io.BufferedWriter;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
@SupportedSourceVersion(SourceVersion.RELEASE_7)
@SupportedAnnotationTypes("com.mtime.annotations.BindView")
public class ButterKnifeProcessor extends AbstractProcessor {
private Messager mMessage;
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
mMessage = processingEnvironment.getMessager();
}
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
for(Element element : roundEnvironment.getElementsAnnotatedWith(BindView.class)){
if(element.getKind() == ElementKind.FIELD){
mMessage.printMessage(Diagnostic.Kind.NOTE,"printMessage : " + element.toString());
}
}
return true;
}
@Override
public Set<String> getSupportedAnnotationTypes() {
return super.getSupportedAnnotationTypes();
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
}
ButterKnifeProcessor 注解處理器中所有方法介紹如下:
init():會(huì)自動(dòng)被注解處理工具調(diào)用,并傳入ProcessingEnviroment參數(shù)强饮,通過該參數(shù)可以獲取到很多有用的工具類:** Elements , Types , Filer **等等
process(Set<? extends TypeElement> annoations, RoundEnvironment roundEnv):Annotation Processor掃描出的結(jié)果會(huì)存儲(chǔ)進(jìn)roundEnv中由桌,可以在這里獲取到注解內(nèi)容,編寫你的操作邏輯邮丰。注意,process()函數(shù)中不能直接進(jìn)行異常拋出,否則的話行您,運(yùn)行Annotation Processor的進(jìn)程會(huì)異常崩潰,然后彈出一大堆讓人捉摸不清的堆棧調(diào)用日志顯示.
getSupportedAnnotationTypes(): 該函數(shù)用于指定該自定義注解處理器(Annotation Processor)是注冊(cè)給哪些注解的(Annotation),注解(Annotation)指定必須是完整的路徑。
getSupportedSourceVersion():用于指定你的java版本剪廉,一般返回:SourceVersion.latestSupported()
- 注解處理器的注冊(cè)過程
為了能使用注解處理器娃循,需要用一個(gè)服務(wù)器文件來注冊(cè)它,現(xiàn)在我們就來創(chuàng)建這個(gè)服務(wù)器文件斗蒋,首先在processor庫的main目錄下新建resources資源文件夾淮野,接下來在resources文件夾下新建META-INF/services目錄文件夾。最后在META-INF/ services中創(chuàng)建javax.annotation.processing.Processor文件吹泡,文件的內(nèi)容是我們上面定義的com.mtime.processor.ButterKnifeProcessor類的完整路徑骤星。
(3) 主項(xiàng)目中調(diào)用
- 我們?cè)谥黜?xiàng)目中引用annotations和processor兩個(gè)庫,并在gradle配置文件中添加‘a(chǎn)nnotationProcessorOptions.includeCompileClasspath = true’的配置爆哑,如果不添加該配置洞难,編譯時(shí)將會(huì)報(bào)錯(cuò)。
android {
compileSdkVersion 26
defaultConfig {
applicationId "com.mtime.myprocessor"
minSdkVersion 15
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
javaCompileOptions{
annotationProcessorOptions.includeCompileClasspath = true
}
}
- 在Activity中模擬使用BindView注解揭朝,如下所示:
public class MainActivity extends Activity {
@BindView(R.id.tv_text)
TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
最后我們可以看看gradle打印出的日志信息下:
也就是說我們獲取到了包含@BindView注解的field textView队贱,那么我們就可以通過反射的方式對(duì)其進(jìn)行初始化色冀,這也是butterKnife框架的開發(fā)原理。EventBus其原理也是一樣的柱嫌,在編譯的時(shí)候就把注解信息給保存下來锋恬,這就省了我們?cè)诖a中尋找注解的開銷,這也是注解類框架非常流行的原因编丘,使用很小的開銷与学,就可以方便我們不用地去寫一些重復(fù)性的代碼。
github地址:https://github.com/Fredlxy/AnnotationProcessorDemo.git