如何编写Java注解处理器?

27

也许我只是看错了方向,但我认为JSE关于注解处理的文档非常稀少。我想编写一个注解处理器,用于处理带有注解的字符串字段和局部变量,并将它们替换为计算出来的字符串表达式。这应该不会太复杂,但我在javax.annotation.processing的Javadoc中感到相当迷茫。

编辑:我需要在编译时处理注解,因为我想修改生成的代码。它应该将带注解的常量字符串表达式替换为计算出的字符串表达式。


同意,我的入门教程是这个:http://tutorials.jenkov.com/java-reflection/annotations.html - Konrad Reiche
2
你想在编译时还是运行时处理注解?请注意,由于 javac 的愚蠢限制,局部变量上的注解实际上是无用的。 - Paul Bellora
我想在编译时处理它们,因此这显然只适用于常量字符串表达式。 - Christian Schlichtherle
你可能可以使用ANT的replace任务,但这可能取决于"computed"的含义。可以参考这篇帖子 - Paul Bellora
也许这会有用:https://github.com/vbauer/jackdaw - Vladislav Bauer
https://github.com/adrianwalker/multiline-string/blob/984ba3cc69e2a1b5a73b8d9dc092ae0adcbb863e/multiline-string/src/main/java/org/adrianwalker/multilinestring/MultilineProcessor.java#L39 这个链接中的代码可能与您想要实现的功能有些相似,而且是一个容易被忽视的例子。 - Caesar
3个回答

15

使用编译时注解处理器是无法完成此操作的。编译时注解处理器只能生成新文件(和类),而不能修改现有类。您可以在运行时进行反射,但严格来说,这并不被称为注解处理。此外,您将无法访问局部变量。

如果您想了解如何编写编译时注解处理器,请参阅https://github.com/pellaton/spring-configuration-validation-processor


理论上,您可以使用注释处理器+Apache BCEL(或类似工具)来修改原始的.class文件。但这听起来很混乱。 - vanza
哎呀...什么?我不能在编译之前使用注解框架修改源代码吗? - Christian Schlichtherle
1
我查看了相关的代码。显然它并不生成代码,只是验证代码。我希望在实际的代码生成步骤之前能够修改抽象语法树。 - Christian Schlichtherle
1
我认为你唯一的选择是使用字节码操作(例如使用ASM、BCEL等工具)或者一些类路径技巧,比如引用由注解处理器生成的类而不是原始文件之类的东西... - s106mo
@Christian Yea,你可以玩字节码,但不能生成新代码。现在,你可以编写一个注解处理器来生成新代码,但那不会成为“编译”过程的一部分。它就像javadoc一样,创建“新文件”,然后可以编译。这是一个两阶段的过程。 - Will Hartung
1
@christian-schlichtherle 是的,您无法进行修改。请查看apt文档。 "这些反射API提供了一个基于源代码的构建时只读程序结构视图。" "运行注解处理器可以生成新的源代码和其他文件"。 - Philippe Marschall

8
这是两个实现此功能的工具:Project LombokDuctileJ。这两个工具在最初问出该问题时已经存在,现在肯定还有其他工具可用。
关键思想是编写一个注解处理器,在编译期间在代码生成之前遍历和修改程序的AST(抽象语法树)。编译器并不会更改磁盘上的源代码,但生成的.class文件将反映您的注解处理器所做的更改。
您可以尝试使用其中一个工具来适应您的需求,或者您也可以自己实现一个工具,并从它们的实现技术中获取灵感。
与类文件处理相比,编译时处理具有两个优点。其一是编译器通常拥有比已编译代码更多的信息。另一个则是所有操作都在编译期间完成,而不需要开发人员在编译后运行单独的工具来重写.class文件。

2
当然,值得一提的是,两者都使用了一种hack来修改AST,利用了Java中当前注解处理器中的一个bug,利用了内部javac API,这些API在未来的JDK版本中可能会被修复/删除(这对我来说是一个很大的劣势)。 - Neil Stockton
很抱歉,尽管这个答案可能会有所帮助,但它的陈述并不准确。提到的工具不是切面处理器,而是编译器插件(完全不同的方法,在某些工具中支持程度不如切面处理器)。你不能使用切面处理器修改AST——只能在新的编译单元(例如新类)中生成新代码。 - pwes

4

我已经查看了Javassist和ASM。也许它们能胜任这项工作,但它们都是关于字节码操作的。我非常希望使用一种允许我操纵抽象语法树的工具。字节码操作只会是我的最后选择。 - Christian Schlichtherle
好的,在调查了各种选项之后,似乎字节码操作是唯一可行的选择。下一个挑战是将其集成到Maven构建中 - 不仅仅是为了我自己,也是为了我的库的用户们。 - Christian Schlichtherle

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接