Java 注解处理器(Annotation Processor)是编译期处理注解的核心工具,它允许开发者在编译时扫描和处理注解,生成额外的代码或资源,从而减少手动编写样板代码的工作量。
一、注解处理器的基本原理
- 处理阶段:注解处理器在编译期(javac 编译时)运行,早于代码生成和类加载阶段。
- 处理对象:处理器可以读取、解析和处理源代码中的注解,并生成新的源文件、配置文件等。
- 隔离性:注解处理器运行在独立的 JVM 进程中,不影响目标程序的运行时行为。
二、核心组件与 API
1. 核心接口
Processor
:所有注解处理器必须实现的接口。AbstractProcessor
:Java 提供的抽象基类,简化处理器开发。
2. 元数据工具
Elements
:处理程序元素(如类、方法、字段)的工具。Types
:处理类型信息的工具。Filer
:创建新文件的工具(如生成源文件)。
3. 注解元数据
RoundEnvironment
:提供当前处理轮次的环境信息,包括被注解的元素。
三、开发步骤
- 定义注解:使用
@interface
定义注解,并设置@Retention(RetentionPolicy.SOURCE)
。 - 实现处理器:继承
AbstractProcessor
,实现处理逻辑。 - 注册处理器:通过
META-INF/services/javax.annotation.processing.Processor
文件注册。 - 编译时触发:在编译时,注解处理器会自动扫描并处理源代码中的注解。
四、综合案例:实现一个简单的 Getter/Setter 生成器
下面通过一个完整的案例,展示如何开发一个自定义注解处理器,自动生成 Java Bean 的 Getter/Setter 方法。
1. 定义注解
// Getter.java
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.SOURCE)
public @interface Getter {
}
// Setter.java
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.SOURCE)
public @interface Setter {
}
// Data.java (组合注解)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface Data {
}
2. 实现注解处理器
// AccessorProcessor.java
import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.*;
@SupportedAnnotationTypes({
"com.example.Getter",
"com.example.Setter",
"com.example.Data"
})
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class AccessorProcessor extends AbstractProcessor {
private Filer filer;
private Messager messager;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
filer = processingEnv.getFiler();
messager = processingEnv.getMessager();
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
// 处理@Data注解的类
for (Element element : roundEnv.getElementsAnnotatedWith(Data.class)) {
if (element instanceof TypeElement) {
TypeElement typeElement = (TypeElement) element;
generateAccessors(typeElement, true, true);
}
}
// 处理@Getter注解的字段
for (Element element : roundEnv.getElementsAnnotatedWith(Getter.class)) {
if (element instanceof VariableElement) {
VariableElement field = (VariableElement) element;
TypeElement classElement = (TypeElement) field.getEnclosingElement();
generateAccessors(classElement, true, false);
}
}
// 处理@Setter注解的字段
for (Element element : roundEnv.getElementsAnnotatedWith(Setter.class)) {
if (element instanceof VariableElement) {
VariableElement field = (VariableElement) element;
TypeElement classElement = (TypeElement) field.getEnclosingElement();
generateAccessors(classElement, false, true);
}
}
return true;
}
private void generateAccessors(TypeElement classElement, boolean generateGetters, boolean generateSetters) {
String className = classElement.getSimpleName().toString();
String packageName = processingEnv.getElementUtils().getPackageOf(classElement).toString();
try {
JavaFileObject jfo = filer.createSourceFile(packageName + "." + className + "_Accessor");
try (PrintWriter writer = new PrintWriter(jfo.openWriter())) {
writer.println("package " + packageName + ";");
writer.println();
writer.println("public class " + className + "_Accessor {");
writer.println();
// 生成构造方法
writer.println(" private final " + className + " target;");
writer.println();
writer.println(" public " + className + "_Accessor(" + className + " target) {");
writer.println(" this.target = target;");
writer.println(" }");
writer.println();
// 生成Getter和Setter方法
for (Element element : classElement.getEnclosedElements()) {
if (element instanceof VariableElement) {
VariableElement field = (VariableElement) element;
String fieldName = field.getSimpleName().toString();
String fieldType = field.asType().toString();
// 生成Getter
if (generateGetters && (
field.getAnnotation(Getter.class) != null ||
classElement.getAnnotation(Data.class) != null)) {
writer.println(" public " + fieldType + " get" + capitalize(fieldName) + "() {");
writer.println(" return target." + fieldName + ";");
writer.println(" }");
writer.println();
}
// 生成Setter
if (generateSetters && (
field.getAnnotation(Setter.class) != null ||
classElement.getAnnotation(Data.class) != null)) {
writer.println(" public void set" + capitalize(fieldName) + "(" + fieldType + " " + fieldName + ") {");
writer.println(" target." + fieldName + " = " + fieldName + ";");
writer.println(" }");
writer.println();
}
}
}
writer.println("}");
}
} catch (IOException e) {
messager.printMessage(Diagnostic.Kind.ERROR, "生成文件失败: " + e.getMessage());
}
}
private String capitalize(String name) {
if (name == null || name.isEmpty()) {
return name;
}
return Character.toUpperCase(name.charAt(0)) + name.substring(1);
}
}
3. 注册处理器
在src/main/resources/META-INF/services
目录下创建javax.annotation.processing.Processor
文件,内容为:
com.example.AccessorProcessor
4. 使用注解
// User.java
@Data
public class User {
private String name;
private int age;
private boolean active;
}
// Main.java
public class Main {
public static void main(String[] args) {
User user = new User();
User_Accessor accessor = new User_Accessor(user);
accessor.setName("John");
accessor.setAge(30);
System.out.println("Name: " + accessor.getName());
System.out.println("Age: " + accessor.getAge());
}
}
5. 编译和运行
-
编译注解处理器模块:
javac -d processor-output src/com/example/*.java src/META-INF/services/javax.annotation.processing.Processor jar cvf processor.jar -C processor-output .
-
使用处理器编译应用代码:
javac -cp processor.jar -processor com.example.AccessorProcessor src/com/example/*.java
-
运行程序:
java com.example.Main
五、注解处理器的高级特性
1. 增量处理(Java 8+)
通过@SupportedOptions
和processingEnv.getOptions()
支持编译选项。
2. 生成非 Java 文件
使用Filer
创建资源文件、配置文件等。
3. 错误处理
通过Messager
输出编译期警告和错误:
messager.printMessage(Diagnostic.Kind.ERROR, "字段名不能为null", element);
六、注意事项
- 性能考虑:复杂的注解处理器可能影响编译速度。
- 处理轮次:处理器可能被多次调用(每个注解类型一次),需处理好状态管理。
- 兼容性:不同版本的 Java 编译器可能对注解处理器有不同的支持。
通过注解处理器,开发者可以实现代码生成、编译时检查等功能,极大提高开发效率,这也是 Lombok、Dagger 等框架的核心技术之一。