注解系列
注解基礎
APT
JavaPoet
前1篇介紹了注解的基本知識和常見用法,由于運行期(RunTime)利用反射去獲得信息還是比較消耗性能的,本篇將介紹1種使用注解更加優雅的方式,編譯期(Compile time)注解,和處理編譯期注解的手段APT和Javapoet,限于篇幅,本篇側重介紹APT
首先你的注解需要聲明為CLASS
@Retention(RetentionPolicy.CLASS)
編譯期解析注解基本原理:
在某些代碼元素上(如類型、函數、字段等)添加注解,在編譯時編譯器會檢查AbstractProcessor的子類,并且調用該類型的process函數,然后將添加了注解的所有元素都傳遞到process函數中,使得開發人員可以在編譯器進行相應的處理,例如,根據注解生成新的Java類,這也就是ButterKnife等開源庫的基本原理。
在處理編譯器注解的第1個手段就是APT(Annotation Processor Tool),即注解處理器。在java5的時候已存在,但是java6開始的時候才有可用的API,最近才隨著butterknife這些庫流行起來。本章將論述甚么是注解處理器,和如何使用這個強大的工具。
甚么是APT
APT是1種處理注解的工具,確切的說它是javac的1個工具,它用來在編譯時掃描和處理注解,1個注解的注解處理器,以java代碼(或編譯過的字節碼)作為輸入,生成.java文件作為輸出,核心是交給自己定義的處理器去處理,
如何使用
每一個自定義的處理器都要繼承虛處理器,實現其關鍵的幾個方法
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() { }
}
下面重點介紹下這幾個函數:
init(ProcessingEnvironment env)
: 每個注解處理器類都必須有1個空的構造函數。但是,這里有1個特殊的init()方法,它會被注解處理工具調用,并輸入ProcessingEnviroment參數。ProcessingEnviroment提供很多有用的工具類Elements, Types和Filerprocess(Set<? extends TypeElement> annotations, RoundEnvironment env)
: 這相當于每一個處理器的主函數main()。你在這里寫你的掃描、評估和處理注解的代碼,和生成Java文件。輸入參數RoundEnviroment,可讓你查詢出包括特定注解的被注解元素。這是1個布爾值,表明注解是不是已被處理器處理完成,官方原文whether or not the set of annotations are claimed by this processor
,通常在處理出現異常直接返回false、處理完成返回true。getSupportedAnnotationTypes()
: 必須要實現;用來表示這個注解處理器是注冊給哪一個注解的。返回值是1個字符串的集合,包括本處理器想要處理的注解類型的合法全稱。getSupportedSourceVersion()
: 用來指定你使用的Java版本。通常這里返回SourceVersion.latestSupported(),你也能夠使用SourceVersion_RELEASE_6、7、8
由于處理器是javac的工具,因此我們必須將我們自己的處理器注冊到javac中,在之前我們需要提供1個.jar文件,打包你的注解處理器到此文件中,并在在你的jar中,需要打包1個特定的文件 javax.annotation.processing.Processor到META-INF/services路徑下
把MyProcessor.jar放到你的builpath中,javac會自動檢查和讀取javax.annotation.processing.Processor中的內容,并且注冊MyProcessor作為注解處理器。
超級麻煩有木有,不過不要慌,谷歌baba給我們開發了AutoService注解,你只需要引入這個依賴,然后在你的解釋器第1行加上
@AutoService(Processor.class)
然后就能夠自動生成META-INF/services/javax.annotation.processing.Processor文件的。省去了打jar包這些繁瑣的步驟。
APT中的Elements和TypeMirrors
在前面的init()中我們可以獲得以下援用
在注解處理進程中,我們掃面所有的Java源文件。源文件的每個部份都是1個特定類型的Element
先來看1下Element
對編譯器來講 代碼中的元素結構是基本不變的,如,組成代碼的基本元素包括包、類、函數、字段、變量的等,JDK為這些元素定義了1個基類也就是Element
類
Element有5個直接子類,分別代表1種特定類型
==
PackageElement | 表示1個包程序元素,可以獲得到包名等 |
---|---|
TypeParameterElement | 表示1般類、接口、方法或構造方法元素的泛型參數 |
TypeElement | 表示1個類或接口程序元素 |
VariableElement | 表示1個字段、enum 常量、方法或構造方法參數、局部變量或異常參數 |
開發中Element可根據實際情況強轉為以上5種中的1種,它們都帶有各自獨有的方法,以下所示
package com.example; // PackageElement
public class Test { // TypeElement
private int a; // VariableElement
private Test other; // VariableElement
public Test () {} // ExecuteableElement
public void setA ( // ExecuteableElement
int newA // TypeElement
) {}
}
再舉個栗子
上一篇 java 場景總結(二)