Java 注解处理器:APT 与自定义注解
APT(Annotation Processing Tool)是 Java 编译器提供的一种工具,用于在编译期扫描和处理注解,从而生成新的源代码或辅助文件。通过 APT,你可以将繁琐的重复代码(如 ButterKnife、Dagger 的生成逻辑)交给机器自动完成,减少手写代码的工作量。
一、 核心流程原理
理解 APT 的工作机制有助于后续编写代码。其核心逻辑发生在 Java 源代码编译成字节码之前。
graph LR
A["Java Source Code: .java files"] --> B["Java Compiler"]
B --> C{"Is there an annotation?"}
C -- No --> D["Generate .class files"]
C -- Yes --> E["Annotation Processing Tool"]
E --> F["Analyze Elements"]
F --> G["Generate New Source Code: .java files"]
G --> B
E --> H["Generate Metadata: .xml or .txt"]
二、 创建自定义注解
首先定义一个注解,标记在代码中告诉处理器哪些类需要被处理。
- 新建 一个名为
annotations的 Java 模块(或者在你的项目中新建包)。 - 创建 一个名为
BindView的接口文件。 - 编写 代码如下,定义注解的生命周期和作用目标:
package com.example.apt;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* TARGET: 定义注解可以用在什么地方(这里是字段上)
* RETENTION: 定义注解保留到什么时候(这里是源码期,编译后丢弃)
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.SOURCE)
public @interface BindView {
int value();
}
三、 实现注解处理器
处理器负责扫描带有 BindView 注解的代码,并根据注解信息生成新的 Java 文件。
- 新建 一个名为
compiler的 Java 模块。 - 打开 该模块的
build.gradle文件。 - 添加 对 Java 库的支持以及必要的依赖:
apply plugin: 'java-library'
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
// 引入刚才定义的注解模块
implementation project(':annotations')
// Google 提供的自动注册服务,简化注册步骤
implementation 'com.google.auto.service:auto-service:1.0-1'
}
sourceCompatibility = "1.8"
targetCompatibility = "1.8"
- 创建
BindViewProcessor类,继承AbstractProcessor。 - 重写 核心方法,实现生成逻辑:
package com.example.apt;
import com.google.auto.service.AutoService;
import java.io.IOException;
import java.io.Writer;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
// 自动注册注解处理器,无需手动创建 META-INF 文件
@AutoService(Processor.class)
public class BindViewProcessor extends AbstractProcessor {
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
// 初始化工具类,用于打印日志
Messager messager = processingEnv.getMessager();
messager.printMessage(Diagnostic.Kind.NOTE, "BindViewProcessor Init...");
}
// 指定支持的 Java 版本
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
// 指定该处理器支持的注解类型
@Override
public Set<String> getSupportedAnnotationTypes() {
return Set.of(BindView.class.getCanonicalName());
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
// 遍历所有被 @BindView 注解标记的元素
for (Element element : roundEnv.getElementsAnnotatedWith(BindView.class)) {
// 获取被注解的变量名
String variableName = element.getSimpleName().toString();
// 获取被注解的变量所在的类名(包名+类名)
String packageName = processingEnv.getElementUtils().getPackageOf(element).getQualifiedName().toString();
String className = element.getEnclosingElement().getSimpleName().toString();
// 获取注解中的值(即 View 的 ID)
BindView bindView = element.getAnnotation(BindView.class);
int id = bindView.value();
// 生成新类的类名:原类名 + Binding
String newClassName = className + "Binding";
try {
// 创建 Java 文件对象
JavaFileObject sourceFile = processingEnv.getFiler().createSourceFile(packageName + "." + newClassName);
// 获取写入流
Writer writer = sourceFile.openWriter();
// 拼接 Java 代码字符串
writer.write("package " + packageName + ";\n\n");
writer.write("public class " + newClassName + " {\n");
writer.write(" public static void bind(" + className + " target) {\n");
writer.write(" target." + variableName + " = target.findViewById(" + id + ");\n");
writer.write(" }\n");
writer.write("}\n");
// **关闭** 流
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return true;
}
}
四、 配置宿主项目并测试
为了让编译器找到你的处理器,需要在主 App 模块中引用刚才创建的 compiler 模块。
- 打开 主 App 模块的
build.gradle文件。 - 添加 依赖配置:
dependencies {
// ... 其他依赖
// 依赖注解模块(编译时和运行时都可能用到,如果是 SOURCE 级别其实编译期即可)
implementation project(':annotations')
// 依赖编译器模块(仅在编译时需要)
annotationProcessor project(':compiler')
}
- 重新构建 项目(点击 Android Studio 的 Build -> Rebuild Project)。
- 查看 生成的文件。构建成功后,在
build/generated/source/apt/...目录下,你会看到类似MainActivityBinding.java的文件。
五、 使用生成的代码
现在你可以在 Activity 中直接调用生成的代码。
- 打开
MainActivity.java。 - 使用
@BindView标记成员变量。
public class MainActivity extends AppCompatActivity {
@BindView(R.id.tv_hello)
TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 调用 APT 生成的绑定方法
MainActivityBinding.bind(this);
textView.setText("APT 运行成功!");
}
}
六、 常见注解元数据说明
在自定义注解时,需要精确控制注解的行为。以下是最常用的两个元注解及其参数说明。
| 元注解 | 作用范围 | 常用参数与说明 |
|---|---|---|
@Target |
限制注解可以用在程序的哪些元素上 | ElementType.FIELD:字段<br>ElementType.METHOD:方法<br>ElementType.TYPE:类、接口(包括注解类型)或枚举声明 |
@Retention |
定义注解被保留的时间长短 | RetentionPolicy.SOURCE:仅源码保留,编译器忽略<br>RetentionPolicy.CLASS:编译期保留,JVM 运行时忽略(默认)<br>RetentionPolicy.RUNTIME:运行期保留,可通过反射获取 |

暂无评论,快来抢沙发吧!