美文网首页Android
注解处理器(Annotation Processor)原理简析

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

作者: Whyn | 来源:发表于2017-04-04 23:50 被阅读1041次

    如果不知道注解处理器(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应用程序.

    相关文章

      网友评论

        本文标题:注解处理器(Annotation Processor)原理简析

        本文链接:https://www.haomeiwen.com/subject/wvfzottx.html