写在前面
下一篇:java AbstractProcessor 编译时注解(API)
Lombok 的getter、setter如何实现的?
答案就在AbstractProcessor 身上,继承AbstractProcessor 抽象类,java文件在编译时编译器会检查AbstractProcessor的子类,并根据这些子类的内容,对java文件进行动态修改,再生成class文件。
1、实现编译时注解
注意:不要将AbstractProcessor 和 使用该AbstractProcessor 的类写在同一个项目中
1.1、实现步骤
以maven项目为例(将实现编译时注解的功能放到项目b ,测试使用这个功能在项目a)
项目结构:
1.1.1、项目b的内容
b的pom.xml
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<!-- 提供@AutoService注解,让AbstractProcessor子类被编译器发现 -->
<!-- 如果不想使用@AutoService注解,也可以在resources/META-INF/services/javax.annotation.processing.Processor中添加AbstractProcessor的子类 -->
<dependency>
<groupId>com.google.auto.service</groupId>
<artifactId>auto-service</artifactId>
<version>1.0-rc5</version>
</dependency>
<!-- 提供JCTree等一些功能api -->
<dependency>
<groupId>com.sun</groupId>
<artifactId>tools</artifactId>
<version>1.8</version>
<scope>system</scope>
<systemPath>${java.home}/../lib/tools.jar</systemPath>
</dependency>
</dependencies>
b的HelloWorld.java
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.SOURCE)
public @interface HelloWorld {
}
b的MyProcessor.java
import com.google.auto.service.AutoService;
import com.sun.tools.javac.model.JavacElements;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.List;
import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import java.util.Iterator;
import java.util.Set;
@SupportedAnnotationTypes("cc.HelloWorld")
@SupportedSourceVersion(SourceVersion.RELEASE_7)
@AutoService(Processor.class)
public class MyProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
Context context = ((JavacProcessingEnvironment) this.processingEnv).getContext();
JavacElements elementUtils = (JavacElements) this.processingEnv.getElementUtils();
TreeMaker treeMaker = TreeMaker.instance(context);
JCTree.JCMethodDecl jcMethodDecl;
for (Iterator var7 = roundEnv.getElementsAnnotatedWith(HelloWorld.class).iterator(); var7.hasNext(); jcMethodDecl.body = treeMaker.Block(0L, List.of(treeMaker.Exec(treeMaker.Apply(List.nil(), treeMaker.Select(treeMaker.Select(treeMaker.Ident(elementUtils.getName("System")), elementUtils.getName("out")), elementUtils.getName("println")), List.of(treeMaker.Literal("这是HelloWorld打印的")))), jcMethodDecl.body))) {
Element element = (Element) var7.next();
jcMethodDecl = (JCTree.JCMethodDecl) elementUtils.getTree(element);
treeMaker.pos = jcMethodDecl.pos;
}
return false;
}
}
1.1.2、项目a的内容
a的pom.xml
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<!-- 引入有编译时注解 @HelloWorld 的包 -->
<dependency>
<groupId>org.example</groupId>
<artifactId>b</artifactId>
<version>1.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
a的Test.java
import cc.HelloWorld;
public class Test {
@HelloWorld
public static void main(String[] args) {
System.out.println("这是Test自己打印的");
}
}
启动Test试试
看一下编译后的文件Test.class
Test.class有两行System.out.println,一行是Test.java原本就有的,一行是@HelloWorld注解动态加入的
1.2、问题与报错
1、java: 服务配置文件不正确, 或构造处理程序对象javax.annotation.processing.Processor…
解决方法:这可能是由于动态注解的代码有改动,但未重新编译class文件造成的,删掉target重新编译即可
2、注解没效果
解决方法:你可能将AbstractProcessor的定义与使用放在了同一个项目中,不要将AbstractProcessor 和 使用该AbstractProcessor 的类写在同一个项目中,会因为AbstractProcessor 没有预编译导致报错或没效果