Compile Doc Processor

Java 可以在编译阶段对类的信息进行解析。

阅读要求:最 Java 注解熟悉。

Simple Demo

Coding

简单例子,目录结构如下:

├─src
│  ├─main
│  │  ├─java
│  │  │  └─com
│  │  │      └─ryo
│  │  │          └─jdk
│  │  │              └─annotation
│  │  │                  │  readme.md
│  │  │                  │
│  │  │                  ├─annotation
│  │  │                  │      Doc.java
│  │  │                  │
│  │  │                  └─processor
│  │  │                          DocProcessor.java
│  │  │
│  │  └─resources
│  │      └─META-INF
│  │          └─services
│  │                  javax.annotation.processing.Processor
│  │
│  └─test
│      └─java
│              DocTest.java
  • Doc.java
/**
 * Doc 文档注解
 * Created by bbhou on 2017/9/29.
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Inherited
public @interface Doc {

    /**
     * 内容
     * @return
     */
    String value() default "";

}
  • DocProcessor.java
/**
 * 编译时注解
 * Created by bbhou on 2017/9/29.
 */
@SupportedSourceVersion(SourceVersion.RELEASE_7)
@SupportedAnnotationTypes("com.ryo.jdk.annotation.annotation.Doc")
public class DocProcessor extends AbstractProcessor {

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        for (TypeElement te : annotations) {
            for (Element e : roundEnv.getElementsAnnotatedWith(te)) {
                String value = e.getAnnotation(Doc.class).value();
                System.out.println(">>>>>>>>>>>>>>> sout value: "+value); 
                //1. 如果打印 error, 直接直接中断。
                processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, ">>>>>>>>>>>> Doc value = " + value);
            }
        }
        return true;
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }

}

只有声明是不够的,还需要将上述对于 @annotation 的解释器进行注册,在 javax.annotation.processing.Processor 文件中添加如下内容(完整类包路径):

com.ryo.jdk.annotation.processor.DocProcessor

Compile

我们对上述代码进行打包,以便于其他模块调用。编译时需要禁用当前模块的注解,否则会报错。

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.2</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
                <!-- Disable annotation processing for ourselves.-->
                <compilerArgument>-proc:none</compilerArgument>
                <encoding>UTF-8</encoding>
            </configuration>
        </plugin>
    </plugins>
</build>

Test

在其他模块,引用当前打包好的 jar。

编写如下测试:

  • DocTest.java
@Doc("hello world")
public class DocTest {
}

然后对代码进行编译:

$   mvn clean install

日志截取如下:

...
[INFO] Compiling 31 source files to D:\CODE\jdk\jdk-test\target\test-classes
>>>>>>>>>>>>>>> sout value: hello world
...
  • processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, ">>>>>>>>>>>> Doc value = " + value);

这段代码没有如预期打印。Diagnostic.Kind.WARNING 如果修改为 Diagnostic.Kind.ERROR,则会编译失败。

适用场景:对某些必须的版本号等信息进行校验,如果不通过,直接编译失败。

Thinking

对于这个编译阶段来说,classes 文件还没有生成,所以想在这里修改对应文件是不太现实的。

这个比较适合对项目中的类信息进行扫描,统计等。