Spring
框架中@Controller、@Service
等等這類註解都是運行時註解
,運行時註解大部分都是通過反射來實現的。而Lombok
是使用編譯時註解實現的。編譯時註解和運行時註解各是什麼呢?-
Java中的註解分爲運行時註解和編譯時註解,運行時註解就是我們經常使用的在程序運行時通過反射得到我們註解的信息,然後再做一些操作。而編譯時註解是什麼呢?就是在程序在編譯期間通過註解處理器進行處理。編譯期:Java語言的編譯期是一段不確定的操作過程,因爲它可能是將
*.java
文件轉化成*.class
文件的過程;也可能是指將字節碼轉變成機器碼的過程;還可能是直接將*.java
編譯成本地機器代碼的過程運行期:從JVM加載字節碼文件到內存中,到最後使用完畢以後卸載的過程都屬於運行期的範疇。 -
註解處理工具apt(Annotation Processing Tool) 正常情況下使用APT工具只是能夠生成一些文件(不僅僅是我們想象的class文件,還包括xml文件等等之類的),並不能修改原有的文件信息,
Lombok
是修改了Java中的**抽象語法樹AST
**才做到了修改其原有類的信息。 -
Processor就是用於處理編譯器註解的類。通過繼承AbstractProcessor就可以自定義處理註解
-
init(ProcessingEnvironment processingEnvironment):初始化方法,這個方法會被註解處理 工具 調用,並傳入一個ProcessingEnvironment變量,這個變量非常重要,它會提供一些非常使用的工具如Elements, Filer, Messager,Types等,後面我們會單獨介紹它們。
-
getSupportedOptions: 這個方法允許我們自定義一些參數傳給Processor,
-
getSupportedAnnotationTypes: 這個方法用於註解的註冊,只有在這個方法中註冊過的註解纔會被註解處理器所處理
-
getSupportedSourceVersion:返回你目前使用的JDK版本,通常返回SourceVersion.latestSupported(),當然如果你沒使用最新的JDK版本的話,也可以返回指定版本
-
process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment):這個方法是Processor中最重要的方法,所有關於註解的處理和文件的生成都是在這個方法中完成的。它有兩個參數,第一個Set<? extends TypeElement> set包含所有待處理的的註解,需要注意的是,如果你定義了一個註解但是沒有在代碼中使用它,這樣是不會加到set中的。第二個參數roundEnvironment表示當前註解所處的環境,通過這個參數可以查詢到當前這一輪註解處理的信息。第一個參數我們通常用不到它,最常用的是roundEnvironment中的getElementsAnnotatedWith方法,這個方法可以返回被特定註解標註的所有元素。process方法還有一個boolean類型的返回值,當返回值爲true的時候表示這個Processor處理的註解不會再被後續的Processor處理。如果返回false,則表示這些註解還會被後續的Processor處理,類似攔截器模式。
-
Processor接口中定義了註解處理器中必要的方法,AbstractProcessor是實現Processor接口的一個抽象類,它在Processor的基礎上提供了三個個註解功能,分別對應上面的三個方法。從下圖中的名字也很容易看出對應的哪些方法。
-
所有被註解標註的部分都會被解析成element,在上面介紹的process方法中,通過roundEnvironment的getElementsAnnotatedWith方法就可以獲取到element的set,element既可能是類,也可能是類屬性,還可能是方法
-
所有被註解標註的部分都會被解析成element,在上面介紹的process方法中,通過roundEnvironment的getElementsAnnotatedWith方法就可以獲取到element的set,element既可能是類,也可能是類屬性,還可能是方法
-
element類型和修飾註解用途【@Target(ElementType.TYPE)】的對比
-
拿到對應Element之後,還需要收集Element的相關信息,下面我們介紹幾個常用的方法
-
getSimpleName:獲取該元素的名字
-
getModifiers:獲取該元素的訪問權限,返回一個Set
-
asType: 獲取該元素的類型,比如TextView會返回android.widget.TextView
-
getEnclosingElement:獲取父級元素,比如參數的父級是方法,方法的父級是類或者接口。
-
getEnclosedElements: 這個和上面的方法是對應的,獲取當前元素的一級子級元素列表。
-
ProcessingEnvironment類
-
getLocale:返回Locale對象,這個沒什麼可說的,就是國際化的東西
-
getSourceVersion:支持的Java版本
-
getOptions:這個在上面介紹getSupportedOptions有用到過,就是用來接收外部參數的
-
getMessager:返回一個Messager對象,Messager是一個分等級的log工具,一共分爲 NOTE,WARNING,MANDATORY_WARNING,ERROR,OTHER 五個等級。實際上在Processor中我們也可以使用sout的方式打印信息,我們可以通過一段代碼測試它們之間的區別
-
getElementUtils:返回一個Elements對象,和Element相關的工具類。比如我們要獲取包名怎麼辦?可以通過上面介紹過的getEnclosingElement方法一層一層網上找,非常麻煩也很容易出錯。還可以通過Elements中的getPackageOf方法直接獲取到
-
getTypeUtils:返回一個Types對象,和元素類型相關的工具類
-
getFiler:返回一個Filer對象,負責生成文件,裏面的方法很少只有4個,我們要生成java文件的時候就調用createClassFile方法就可以了。
實戰
先編譯 javac compileTimeAnnotation/lombok/MyGett*
再執行javac -processor compileTimeAnnotation.lombok.MyGetterProcessor compileTimeAnnotation/lombok/TestAno.java
參考:
https://mp.weixin.qq.com/s/qde6zvqbE84gDcw_zEo62g
https://www.codercto.com/a/92257.html