786 字
2 分钟
Java注解处理器详解
说实话,刚接触注解处理器的时候我是有点懵的——编译时还能跑代码生成代码?后来在项目里用了一回,自动生成Builder类和Mapper,那种”原来还能这么玩”的感觉是真上头。这篇文章就聊聊我踩过的坑和总结下来的经验。
Java注解处理器详解
什么是注解处理器?
注解处理器(Annotation Processor)就是Java编译器的一个插件,在编译的时候扫描和处理注解,自动生成代码。Lombok的@Getter、@Setter就是靠这个实现的,刚知道的时候我还在想”这玩意儿是魔法吧”。
工作原理
- 编译器扫描源码,收集所有注解
- 把注解传给对应的处理器
- 处理器处理注解,生成新的源码或资源文件
- 编译器继续编译生成的代码
创建注解处理器的步骤
1. 定义注解
@Target(ElementType.TYPE)@Retention(RetentionPolicy.SOURCE)public @interface GenerateBuilder {}2. 实现处理器
继承AbstractProcessor,重写process方法:
@SupportedAnnotationTypes("com.example.GenerateBuilder")@SupportedSourceVersion(SourceVersion.RELEASE_8)public class BuilderProcessor 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) { for (TypeElement annotation : annotations) { for (Element element : roundEnv.getElementsAnnotatedWith(annotation)) { if (element instanceof TypeElement) { generateBuilder((TypeElement) element); } } } return true; }
private void generateBuilder(TypeElement typeElement) { // 生成Builder类的代码 // 用filer.createSourceFile()创建新文件 }}3. 注册处理器
在META-INF/services/javax.annotation.processing.Processor文件中写入处理器的全限定类名。
或者用Google的auto-service库,加个@AutoService(Processor.class)注解就行,省得手动写配置文件。
核心API
- ProcessingEnvironment:处理器与编译器的通信渠道
getFiler():创建新文件getMessager():输出日志/错误信息getElementUtils():操作元素(类、方法、字段等)getTypeUtils():操作类型
- RoundEnvironment:当前轮次的信息
getElementsAnnotatedWith():获取被注解标记的元素
生成代码的示例
生成Builder类的核心逻辑就是用Filer创建Java源文件,然后用字符串拼接的方式写入代码。虽然看起来有点原始,但这就是注解处理器的标准做法。
JavaFileObject javaFileObject = filer.createSourceFile(packageName + "." + builderClassName);try (Writer writer = javaFileObject.openWriter()) { writer.write("package " + packageName + ";\n\n"); writer.write("public class " + builderClassName + " {\n"); // ... 生成字段、setter方法、build方法 writer.write("}\n");}注意事项
- 避免无限循环——如果处理器生成的代码也包含被处理的注解,就会陷入死循环。这个坑我真踩过——生成代码里忘了去掉注解,结果编译的时候CPU飙到100%,我还以为是电脑中毒了
- 跨模块处理——处理器只能处理当前模块的代码,不能处理其他模块的
- 调试困难——在编译时运行,没法直接打断点。可以用
Messager输出日志来调试 - 多轮处理——处理器可能会被调用多次,因为生成的代码也需要被处理
替代方案
- Lombok:最流行的注解处理器,减少样板代码
- JavaParser:解析和修改Java源代码
- ByteBuddy/ASM:运行时生成和修改字节码
💡 实战贴士: 如果你只是想减少样板代码(getter/setter/toString),直接用Lombok就够了,别自己写注解处理器。注解处理器的真正价值在于生成重复的业务代码,比如根据实体类自动生成Mapper接口、Builder类等。这种场景下自己写一个处理器,能省下大量重复劳动。
分享
如果这篇文章对你有帮助,欢迎分享给更多人!
部分信息可能已经过时
相关文章 智能推荐
1
Java注解详解
Java 刚学Java时觉得注解不就是几个@符号嘛,后来用Spring Boot才发现注解能玩出这么多花样——从编译检查到运行时注入,注解远不止看起来那么简单。
2
Java异常处理详解
Java try-catch是Java最基础的异常处理手段,但什么时候该用受检异常、什么时候该抛RuntimeException,这中间有不少门道值得琢磨。
3
Java NIO详解
Java NIO的Selector模型让我第一次感受到了单线程处理万级连接的魅力。相比传统IO的阻塞读写,Channel加Buffer的组合简直是为高性能网络编程量身定做。
4
Java日期和时间处理详解
Java Java的日期时间API进化史简直就是一部踩坑史——从Date到Calendar再到java.time包,每次改版都在修复上一版的槽点。这篇文章梳理了各个版本的用法和坑。
5
Java反射机制详解
Java 反射是Java高级特性中最让我着迷的一个——运行时能看见自己。工作中用Spring的依赖注入时总在想它是怎么做到的,后来才明白反射就是那个幕后推手。






