如果不知道注解處理器(Annotation Processor),可以先查看一下我上一篇寫的注解處理器(Annotation Processor)簡(jiǎn)析,先了解下Annotation Processor的用法,再來(lái)看下原理.
首先,我們知道,自定義的Annotation Processor都要繼承于類AbstractProcessor,那么,我們來(lái)簡(jiǎn)略看下AbstractProcessor的文檔介紹:
可以看到,AbstractProcessor實(shí)現(xiàn)了接口Processor,那么,我們?cè)趤?lái)看下Processor的api文檔:
javax.annotation.processing
Interface ProcessorAll Known Implementing Classes:
AbstractProcessor
public interface Processor
The interface for an annotation processor.
Annotation processing happens in a sequence of rounds. On each round, a processor may be asked to process a subset of the annotations found on the source and class files produced by a prior round. The inputs to the first round of processing are the initial inputs to a run of the tool; these initial inputs can be regarded as the output of a virtual zeroth round of processing. If a processor was asked to process on a given round, it will be asked to process on subsequent rounds, including the last round, even if there are no annotations for it to process. The tool infrastructure may also ask a processor to process files generated implicitly by the tool's operation.
Each implementation of a Processor must provide a public no-argument constructor to be used by tools to instantiate the processor. The tool infrastructure will interact with classes implementing this interface as follows:
- If an existing Processor object is not being used, to create an instance of a processor the tool calls the no-arg constructor of the processor class.
- Next, the tool calls the init
method with an appropriate ProcessingEnvironment .- Afterwards, the tool calls getSupportedAnnotationTypes
, getSupportedOptions
, and getSupportedSourceVersion
. These methods are only called once per run, not on each round.- As appropriate, the tool calls the process
method on the Processor object; a new Processor object is not created for each round.If a processor object is created and used without the above protocol being followed, then the processor's behavior is not defined by this interface specification.The tool uses a discovery process to find annotation processors and decide whether or not they should be run. By configuring the tool, the set of potential processors can be controlled. For example, for a JavaCompiler
the list of candidate processors to run can be set directly or controlled by a search path used for a service-style lookup. Other tool implementations may have different configuration mechanisms, such as command line options; for details, refer to the particular tool's documentation. Which processors the tool asks to run is a function of what annotations are present on the root elements, what annotation types a processor processes, and whether or not a processor claims the annotations it processes. A processor will be asked to process a subset of the annotation types it supports, possibly an empty set. For a given round, the tool computes the set of annotation types on the root elements. If there is at least one annotation type present, as processors claim annotation types, they are removed from the set of unmatched annotations. When the set is empty or no more processors are available, the round has run to completion. If there are no annotation types present, annotation processing still occurs but only universal processors which support processing "*"
can claim the (empty) set of annotation types.
Note that if a processor supports "*"
and returns true
, all annotations are claimed. Therefore, a universal processor being used to, for example, implement additional validity checks should return false
so as to not prevent other such checkers from being able to run.
If a processor throws an uncaught exception, the tool may cease other active annotation processors. If a processor raises an error, the current round will run to completion and the subsequent round will indicate an error was raised. Since annotation processors are run in a cooperative environment, a processor should throw an uncaught exception only in situations where no error recovery or reporting is feasible.
The tool environment is not required to support annotation processors that access environmental resources, either per round or cross-round, in a multi-threaded fashion.
If the methods that return configuration information about the annotation processor return null
, return other invalid input, or throw an exception, the tool infrastructure must treat this as an error condition.
To be robust when running in different tool implementations, an annotation processor should have the following properties:
- The result of processing a given input is not a function of the presence or absence of other inputs (orthogonality).
- Processing the same input produces the same output (consistency).
- Processing input A followed by processing input B is equivalent to processing B then A(commutativity)
- Processing an input does not rely on the presence of the output of other annotation processors (independence)
The Filer
interface discusses restrictions on how processors can operate on files.
Note that implementors of this interface may find it convenient to extend AbstractProcessor
rather than implementing this interface directly.Since:
1.6
注解處理發(fā)生在一系列回合中.每個(gè)回合中,注解處理器都有可能被叫去處理由上一次注解產(chǎn)生的源碼和類文件中的找到的注解子集.第一次注解處理回合的輸入就是工具第一次運(yùn)行的輸入;這些初始輸入可以認(rèn)為是一個(gè)虛擬的第零次注解處理回合的輸出.如果注解處理器被叫去處理一個(gè)特定的回合,那么接下來(lái)的回合它都會(huì)繼續(xù)處理,即使后續(xù)回合沒(méi)有它需要處理的注解.注解處理器有可能會(huì)被叫去處理由(編譯)工具隱式生成的文件.每個(gè)注解處理器的實(shí)現(xiàn)都必須提供一個(gè)公有的無(wú)參構(gòu)造函數(shù),由工具進(jìn)行實(shí)例化.工具會(huì)與實(shí)現(xiàn)該接口(Processor)的類進(jìn)行如下交互:
- 如果一個(gè)已存在的Processor實(shí)例未被使用,(編譯)工具會(huì)調(diào)用注解處理器的無(wú)參構(gòu)造函數(shù)實(shí)例化出一個(gè)Processor對(duì)象.
- 接下來(lái),(編譯)工具會(huì)調(diào)用init函數(shù),并傳入一個(gè)合適的ProcessingEnvironment.
- 之后,(編譯)工具會(huì)調(diào)用getSupportedAnnotationTypes,getSupportedOptions和getSupportedSourceVersion.這些方法只會(huì)在每一次運(yùn)行時(shí)被調(diào)用一次,而不會(huì)在每個(gè)注解回合都被調(diào)用.
- 正常情況下,(編譯)工具會(huì)調(diào)用注解處理器實(shí)例的process函數(shù);每個(gè)注解回合并不會(huì)產(chǎn)生新的注解實(shí)例.
如果一個(gè)注解處理器實(shí)例被創(chuàng)建,但是使用卻沒(méi)有遵循上述協(xié)議,那么這個(gè)注解處理器的行為并未被該接口規(guī)范定義.(編譯)工具使用搜索程序去找到注解處理器并決定它們是否得以運(yùn)行.通過(guò)配置(編譯)工具,潛在的注解處理器可以被控制.比如,對(duì)于javaCompiler,候選處理器可以直接被指定或者通過(guò)使用service-style查找指定搜索路徑進(jìn)行控制.其他(編譯)工具可以具有不同的配置機(jī)制,比如控制行選項(xiàng);具體點(diǎn)講,參考特定工具文檔.(編譯)工具會(huì)調(diào)用運(yùn)行的注解處理器是由root elements指示的注解,是注解處理器處理的注解類型和注解處理器聲明它要處理的注解的方法.注解處理器會(huì)被叫去處理它支持的注解類型子集,有可能是一個(gè)空的集合.在給定回合,(編譯)工具會(huì)計(jì)算root elements的注解類型集合.如果有最少一個(gè)注解類型存在,就是注解處理器聲明的注解類型之一,它們就會(huì)被從未匹配的注解類型集合中移除.當(dāng)(未匹配)注解集合為空或者沒(méi)有其它的注解處理器,那么該注解處理回合就結(jié)束了.如果沒(méi)有聲明注解類型,只有通用處理器(支持處理"*"聲明(空)所有注解類型集合)仍然會(huì)進(jìn)行注解處理.注意如果一個(gè)注解處理器支持"*"并且返回true,則所有的注解類型都被聲明.因此,一個(gè)通用注解處理器如果被用于實(shí)現(xiàn)附加有效檢驗(yàn),那么應(yīng)該返回false,為了不防止這類檢驗(yàn)器得以運(yùn)行.如果一個(gè)注解處理器拋出了一個(gè)未捕獲異常,(編譯)工具可能會(huì)停止其他活動(dòng)的注解處理器.如果一個(gè)注解處理器引起了一個(gè)錯(cuò)誤,當(dāng)前注解回合會(huì)結(jié)束,并且后續(xù)回合會(huì)指明一個(gè)錯(cuò)誤產(chǎn)生了.因?yàn)樽⒔馓幚砥鞫际沁\(yùn)行在共同協(xié)作的環(huán)境中,只有當(dāng)錯(cuò)誤恢復(fù)或報(bào)告提交是無(wú)法執(zhí)行的情況下,注解處理器才允許拋出一個(gè)未捕獲異常.
(編譯)工具環(huán)境不要求要支持注解處理器能以多線程方式在每一回合或交叉回合能訪問(wèn)環(huán)境資源.
如果返回注解處理器的配置信息的方法返回null,返回其他無(wú)效輸入,或者拋出一個(gè)異常,(編譯)工具必須將這些當(dāng)做是一個(gè)錯(cuò)誤條件.
為了在不同的工具實(shí)現(xiàn)能夠健壯運(yùn)行,注解處理器必須有以下性能:
- 對(duì)于一個(gè)給定的輸入的處理結(jié)果,不影響其他輸入的存在或缺失(正交性)
- 處于相同的輸入會(huì)產(chǎn)生相同的輸出(一致性)
- 先處理輸入A,然后處于輸入B等同于先處理B在處理A(可交換性)
- 處理輸入會(huì)依賴于其他注解處理器的輸出(獨(dú)立性)
Filer接口討論了注解處理器操作文件的限定.
請(qǐng)知悉Processor的實(shí)現(xiàn)通過(guò)繼承AbstractProcessor會(huì)比直接實(shí)現(xiàn)該接口更加方便.
簡(jiǎn)單總結(jié)如下:
- Annotation Processor可能會(huì)被多次調(diào)用.
- Annotation Processor被調(diào)用一次后,后續(xù)若還有注解處理,該Annotation Processor仍然會(huì)繼續(xù)被調(diào)用.
- 自定義Annotation Processor必須帶有一個(gè)無(wú)參構(gòu)造函數(shù),讓javac進(jìn)行實(shí)例化.
- 如果Annotation Processor拋出一個(gè)未捕獲異常,javac可能會(huì)停止其他的Annotation Processor.只有在無(wú)法拋出錯(cuò)誤或報(bào)告的情況下,才允許拋出異常.
- Annotation Processor運(yùn)行在一個(gè)獨(dú)立的jvm中,所以可以將它看成是一個(gè)java應(yīng)用程序.