注解处理器(Annotation Processor)原理简析

如果不知道注解处理器(Annotation Processor),可以先查看一下我上一篇写的注解处理器(Annotation Processor)简析,先了解下Annotation Processor的用法,再来看下原理.

首先,我们知道,自定义的Annotation Processor都要继承于类AbstractProcessor,那么,我们来简略看下AbstractProcessor的文档介绍:


AbstractorProcessor

可以看到,AbstractProcessor实现了接口Processor,那么,我们在来看下Processor的api文档:

javax.annotation.processing
Interface Processor

All 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:

  1. 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.
  2. Next, the tool calls the init
    method with an appropriate ProcessingEnvironment .
  3. Afterwards, the tool calls getSupportedAnnotationTypes
    , getSupportedOptions
    , and getSupportedSourceVersion
    . These methods are only called once per run, not on each round.
  4. 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:

  1. The result of processing a given input is not a function of the presence or absence of other inputs (orthogonality).
  2. Processing the same input produces the same output (consistency).
  3. Processing input A followed by processing input B is equivalent to processing B then A(commutativity)
  4. 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

注解处理发生在一系列回合中.每个回合中,注解处理器都有可能被叫去处理由上一次注解产生的源码和类文件中的找到的注解子集.第一次注解处理回合的输入就是工具第一次运行的输入;这些初始输入可以认为是一个虚拟的第零次注解处理回合的输出.如果注解处理器被叫去处理一个特定的回合,那么接下来的回合它都会继续处理,即使后续回合没有它需要处理的注解.注解处理器有可能会被叫去处理由(编译)工具隐式生成的文件.每个注解处理器的实现都必须提供一个公有的无参构造函数,由工具进行实例化.工具会与实现该接口(Processor)的类进行如下交互:

  1. 如果一个已存在的Processor实例未被使用,(编译)工具会调用注解处理器的无参构造函数实例化出一个Processor对象.
  2. 接下来,(编译)工具会调用init函数,并传入一个合适的ProcessingEnvironment.
  3. 之后,(编译)工具会调用getSupportedAnnotationTypes,getSupportedOptions和getSupportedSourceVersion.这些方法只会在每一次运行时被调用一次,而不会在每个注解回合都被调用.
  4. 正常情况下,(编译)工具会调用注解处理器实例的process函数;每个注解回合并不会产生新的注解实例.

如果一个注解处理器实例被创建,但是使用却没有遵循上述协议,那么这个注解处理器的行为并未被该接口规范定义.(编译)工具使用搜索程序去找到注解处理器并决定它们是否得以运行.通过配置(编译)工具,潜在的注解处理器可以被控制.比如,对于javaCompiler,候选处理器可以直接被指定或者通过使用service-style查找指定搜索路径进行控制.其他(编译)工具可以具有不同的配置机制,比如控制行选项;具体点讲,参考特定工具文档.(编译)工具会调用运行的注解处理器是由root elements指示的注解,是注解处理器处理的注解类型和注解处理器声明它要处理的注解的方法.注解处理器会被叫去处理它支持的注解类型子集,有可能是一个空的集合.在给定回合,(编译)工具会计算root elements的注解类型集合.如果有最少一个注解类型存在,就是注解处理器声明的注解类型之一,它们就会被从未匹配的注解类型集合中移除.当(未匹配)注解集合为空或者没有其它的注解处理器,那么该注解处理回合就结束了.如果没有声明注解类型,只有通用处理器(支持处理"*"声明(空)所有注解类型集合)仍然会进行注解处理.注意如果一个注解处理器支持"*"并且返回true,则所有的注解类型都被声明.因此,一个通用注解处理器如果被用于实现附加有效检验,那么应该返回false,为了不防止这类检验器得以运行.如果一个注解处理器抛出了一个未捕获异常,(编译)工具可能会停止其他活动的注解处理器.如果一个注解处理器引起了一个错误,当前注解回合会结束,并且后续回合会指明一个错误产生了.因为注解处理器都是运行在共同协作的环境中,只有当错误恢复或报告提交是无法执行的情况下,注解处理器才允许抛出一个未捕获异常.
(编译)工具环境不要求要支持注解处理器能以多线程方式在每一回合或交叉回合能访问环境资源.
如果返回注解处理器的配置信息的方法返回null,返回其他无效输入,或者抛出一个异常,(编译)工具必须将这些当做是一个错误条件.
为了在不同的工具实现能够健壮运行,注解处理器必须有以下性能:

  1. 对于一个给定的输入的处理结果,不影响其他输入的存在或缺失(正交性)
  2. 处于相同的输入会产生相同的输出(一致性)
  3. 先处理输入A,然后处于输入B等同于先处理B在处理A(可交换性)
  4. 处理输入会依赖于其他注解处理器的输出(独立性)

Filer接口讨论了注解处理器操作文件的限定.
请知悉Processor的实现通过继承AbstractProcessor会比直接实现该接口更加方便.

简单总结如下:

  • Annotation Processor可能会被多次调用.
  • Annotation Processor被调用一次后,后续若还有注解处理,该Annotation Processor仍然会继续被调用.
  • 自定义Annotation Processor必须带有一个无参构造函数,让javac进行实例化.
  • 如果Annotation Processor抛出一个未捕获异常,javac可能会停止其他的Annotation Processor.只有在无法抛出错误或报告的情况下,才允许抛出异常.
  • Annotation Processor运行在一个独立的jvm中,所以可以将它看成是一个java应用程序.
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容