java注解本身不执行逻辑,但通过自定义注解处理器(继承abstractprocessor)并在编译期扫描@retention(retentionpolicy.source)注解,可生成新源文件,实现如butterknife、lombok等框架的代码生成功能。

Java 注解本身不执行逻辑,但配合自定义注解处理器(AnnotationProcessor),可以在编译期(javac 阶段)扫描源码中的特定注解,并生成新的 Java 源文件(如 XXX$$Generated.java),实现类似 ButterKnife、Lombok、Room、Dagger 的代码生成能力。
一、定义自定义注解(需声明保留策略为 SOURCE)
注解必须用 @Retention(RetentionPolicy.SOURCE),确保它只保留在源码阶段,不会进入字节码或运行时 —— 因为注解处理器只在编译期工作。
示例:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface AutoLog {
String value() default "";
}
-
必须加
@Target明确作用位置(如ElementType.TYPE表示类上) -
不能用
CLASS或RUNTIME,否则注解处理器可能收不到(部分构建工具如 Gradle 会跳过非SOURCE注解的处理)
二、编写注解处理器(继承 AbstractProcessor)
核心是重写 process() 方法,在其中遍历被注解的元素,用 Filer 生成新源文件。
示例:为每个 @AutoLog 类生成一个日志辅助类:
public class AutoLogProcessor extends AbstractProcessor {
private Messager messager;
private Filer filer;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
this.messager = processingEnv.getMessager();
this.filer = processingEnv.getFiler();
}
@Override
public Set<string> getSupportedAnnotationTypes() {
return Set.of(AutoLog.class.getCanonicalName());
}
@Override
public boolean process(Set extends TypeElement> annotations, RoundEnvironment roundEnv) {
for (Element element : roundEnv.getElementsAnnotatedWith(AutoLog.class)) {
if (element.getKind() != ElementKind.CLASS) continue;
TypeElement typeElement = (TypeElement) element;
String className = typeElement.getSimpleName().toString();
String packageName = processingEnv.getElementUtils()
.getPackageOf(typeElement).getQualifiedName().toString();
try {
JavaFileObject file = filer.createSourceFile(
packageName + "." + className + "$$Logger"
);
try (Writer writer = file.openWriter()) {
writer.write("package " + packageName + ";\n");
writer.write("public class " + className + "$$Logger {\n");
writer.write(" public static void log() { System.out.println(\"Log from " + className + "\"); }\n");
writer.write("}");
}
} catch (IOException e) {
messager.printMessage(Diagnostic.Kind.ERROR, "Failed to generate logger: " + e.getMessage());
}
}
return true; // 告诉编译器该注解已处理完毕
}
}</string>
-
必须重写
getSupportedAnnotationTypes(),返回全限定名(字符串),否则处理器不生效 -
process()返回true表示“我处理了这些注解”,后续编译轮次不会再交由该处理器处理同一批注解 -
用
Messager输出编译期提示/错误,用户能在 IDE 或命令行看到反馈
三、注册处理器(META-INF/services/javax.annotation.processing.Processor)
在处理器模块的 src/main/resources/META-INF/services/ 下创建文件:
Miller (mlr) 是一个命令行工具,用于查询、整形和重新格式化名称索引数据,如 CSV、TSV、JSON 和 JSON Lines。它将 awk、sed、cut、join 和 sort 的功能整合到一个专为结构化数据处理而构建的单一工具中。
javax.annotation.processing.Processor
内容为处理器类的全限定名,例如:
com.example.AutoLogProcessor
- 文件名和路径必须严格匹配,大小写敏感
- 多个处理器换行分隔
四、在项目中使用(Maven / Gradle 配置)
注解和处理器需拆分为两个模块(推荐):一个只放注解(annotation),一个放处理器(processor)。使用方只依赖注解模块,并通过 annotationProcessor 引入处理器。
Gradle 示例(app module):
dependencies {
implementation project(':annotations') // @AutoLog 所在模块
annotationProcessor project(':processor') // AutoLogProcessor 所在模块
}
Maven 示例:
<plugin><groupid>org.apache.maven.plugins</groupid><artifactid>maven-compiler-plugin</artifactid><configuration><annotationprocessorpaths><path><groupid>com.example</groupid><artifactid>auto-log-processor</artifactid><version>1.0</version></path></annotationprocessorpaths></configuration></plugin>
- Gradle 中
annotationProcessor不会打包进最终 APK/JAR,仅参与编译;compileOnly可用于注解模块 - IDE(如 IntelliJ)需开启 “Enable annotation processing” 并配置输出目录(默认
build/generated/source/apt)
不复杂但容易忽略:生成的代码会被编译器自动编译进最终 class,所以你写的 MyClass$$Logger 在运行时可以直接 new 或调用 —— 它和手写类完全等价。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










