Java静态元编程
我想实现注释处理器,它将根据现有的“原型”类生成新类。
import java.util.List
@MyAnnotation
class MySuperClassPrototype {
static MySuperClassPrototype createInstance() {
return new MySuperClassPrototype();
}
}
作为下面代码的结果。将生成以下新源文件(编译单元):
import java.util.List
class MySuperClass {
static MySuperClass createInstance() {
return new MySuperClass();
}
public void specialAddedMethod() {
/*...*/
}
}
我想复制所有顶级导入语句和静态成员,而不是原型类的静态成员。我已经在编译器树 API (com.sun.source.tree) 方面取得了很大进展。我可以打印出 Tree 数据类型,同时用新的类名替换旧的类名。但有些问题看起来相当困难。
如果我在树中获得 Tree.Kind.IDENTIFIER ,我如何找到它引用的实际类。我需要用 MySuperClass 标识符替换所有出现的 MySuperClassPrototype 标识符,然后打印出整个树。
可行吗?
同样,我需要过滤掉 @MyAnnotation 注释,并再次用 Tree.Kind.IDENTIFIER 或 Tree.Kind.MEMBER_SELECT 表示。
如何找到该标识符引用的实际注释类?
另一个问题是打印树。如果我使用 toString 方法,我会得到不错的结果,但构造函数会打印为带有“
您可以查看我在此处提供的代码
I'd like to implement annotation processor that will generate new class based on existing "prototype" class.
import java.util.List
@MyAnnotation
class MySuperClassPrototype {
static MySuperClassPrototype createInstance() {
return new MySuperClassPrototype();
}
}
As a result of code below. The following new source file (compilation unit) will be generated:
import java.util.List
class MySuperClass {
static MySuperClass createInstance() {
return new MySuperClass();
}
public void specialAddedMethod() {
/*...*/
}
}
I'd like to copy all top-level import statements and static members and not static members of prototype-class. I've moved pretty far with Compiler Tree API (com.sun.source.tree). I can print out Tree data-type while substituting new class name for old. But there are problems that seems pretty hard.
If I get Tree.Kind.IDENTIFIER in the tree, how can I find what actual class it references. I need to replace all occurrences of MySuperClassPrototype identifier with MySuperClass identifier, and than print out whole tree.
Is it feasible?
Similarly I need to filter out @MyAnnotation annotation, and again it is represented with Tree.Kind.IDENTIFIER or Tree.Kind.MEMBER_SELECT.
How can I find out actual annotation class that is referenced by this identifier?
And another problem is printing out tree. If I use toString method I got decent result, but constructors are printed as methods with "<init>" name instead of methods with the same name as it's class, so I need to manually print every kind of Tree node.
You can see code I've come with here
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
是的,这是可能的,而且我至少知道两种方法。
首先,“传统”方法是编写 ant 任务/maven 插件/命令行 java 实用程序,扫描给定的文件路径并调用每个类,例如
Class.forName(className).getAnnotations(MyAnnotation.class).如果这不为空,则使用反射发现类并执行您需要的操作。
其他方法稍微困难一些,但更强大。
您可以实现自己的 Processor(实现 javax.annotation.processing.Processor 或什至更好地扩展 javax.annotation.processing.AbstractProcessor)。
您的处理器只需放置到编译器类路径中,它就会在编译器运行时自动运行。您甚至可以配置 IDE(例如 Eclipse)来运行您的处理器。它是java编译器的一种扩展。因此,每次 Eclipse 构建您的项目时,它都会运行处理器并根据您添加的新注释创建所有新类。
请查看此项目作为参考。
Yes, it is possible and I know at least 2 ways.
First, "traditional" way is to write ant task/maven plugin/just command line java utility that scans given file path and calls for each class something like
Class.forName(className).getAnnotations(MyAnnotation.class)
. If this is not null discover class using reflection and do what you need.Other way is a little bit more difficult but more powerful.
You can implement your own
Processor
(that implementsjavax.annotation.processing.Processor
or even better extendsjavax.annotation.processing.AbstractProcessor
.Your processor will just have to be placed to the
compiler
classpath and it will run automatically when compiler runs. You can even configure your IDE (e.g. Eclipse) to run your processor. It is a kind of extension to java compiler. So, every time eclipse builds your project it runs the processor and creates all new classes according to new annotations you have added.Please take a look on this project as a reference.
8年了,还没回复。因此,我会尽力回答,让您满意。
我进一步关注问题的静态部分。
TL;DR:
您不会在此答案中找到复制和粘贴代码。
是的,绝对是。
您必须使用注释处理器中的 RoundEnvironment 来获取 TypeElement。
静态元编程
静态元编程(您要求的)是在编译时完成的元编程。作者:Kontrast:动态元编程是在运行时完成的元编程。元编程本身就是程序的设计,将其他程序作为数据处理。
Pfeh,有很多东西需要了解。如果您对这个主题感兴趣,或多或少有一个不错的来源是 维基百科。
您的目标是在编译时生成一个类。对于运行时,可以使用 cglib 之类的东西来完成。但是,由于您选择静态(并且出于所有正确的原因),我不会解释这一点。
您正在寻找的概念是注释处理器。该链接是 Baeldung 的链接,他们在那里完全按照您的需求进行操作,只考虑了构建器模式。您会很高兴听到,这种情况受到高度鼓励,并且可以使用注释处理器 API 轻松实现。它甚至允许您生成代码,该代码再次传递到同一个或另一个注释处理器,而无需您执行任何操作。
在开始之前,尝试用 google 搜索一下“Java Annotation Processing”。那里有很多好的资源,它们会对您有所帮助。太多了,在此一一列举。请注意,注释处理器中的编码与正常编码不同。差别不大,但您正在处理的类尚未创建。所以请记住这一点,不要灰心!
使用注释处理器
您的基本注释处理器将如下所示:
AutoService Annotation 用于动态注册您的注释处理器。它来自外部源,这样您就不会想知道为什么此代码无法编译。
在
处理所有带注释的类
部分中,您有带注释的元素(它们是带注释的类)。您现在必须验证它们是类而不是接口或其他注释。这是因为@Target(ElementType.Type)
针对任何类型,包括接口和注释。此外,您需要验证您需要的任何内容是否存在,或者使用 消息发送器。如果您在此处打印错误(例如),您将停止编译,并且在大多数现代 IDE 中都会看到该错误。可以通过调用 roundEnv.getMessager() 来实现。
之后您可以生成一个新类并将其作为 .java 文件写入编译器的输入。这可以通过使用 Filer。
StackOverflow 中的答案确实没有公正地对待这个主题。我强烈建议查看 Baeldung 示例并试图从那里揭开一些事情。这个 API 与 Java 6 一样古老,但仍然没有得到广泛使用。我鼓励读者您亲自尝试一下:)
8 Years and not yet answered. Because of that, i will try to answer it, to your satisfaction.
I fill furthermore concentrate on the static part of the question.
TL;DR:
You will not find copy and paste code in this answer.
Yes, absolutely.
You will have to use the RoundEnvironment within an Annotation Processor to get the TypeElement.
Static Metaprogramming
Static metaprogramming (which you asked for) is metaprogramming done at compile time. By Kontrast: Dynamic metaprogramming is metaprogramming done at run time. And metaprogramming it self is the design of programs, that handle other programs as data.
Pfeh, a lot to take in. If you are interested in this topic, a more or less good source for that is wikipedia.
Your target would be, to generate a class at compile time. For run time, this would be done with something like cglib. But, since you choose static (and for all the right reasons), i will not explain this.
The concept you are looking for is the annotation processor. The link is a link to Baeldung, where they do exactly, what you are looking for, only with the builder pattern in mind. You will love to hear, that this scenario is highly encouraged and easy to do with the annotation processor API. It even allows you, to generate code, which again is passed to the same or another annotation processor, without you doing anything.
Before jumping right in, try to google yourself about "Java Annotation Processing". There are a lot of good sources out there, which will help you. To much, to list here. Just note, that coding in an annotation processor is different than coding normally. Not a huge difference, but the classes you are working on are not yet created. So keep this in mind and don't get discouraged!
Using the Annotation Processor
Your basic annotation processor would look something like this:
The AutoService Annotation is used, to dynamically register your annotation processor. It comes from an external source, just so you don't wonder, why this code won't compile.
In the
handle all annotated classes
part, you have the annotated Elements (which are the annotated classes). You now would have to verify, that they are classes and not interfaces or other annotations. This is because@Target(ElementType.Type)
aims at any type, which includes interfaces and annotations. Furthermore, you would want to verify, that anything you require is present, or print an error to the compiler using the Messager.If you print an error here (for example), you will stop compiling and the error will be seen in most modern IDEs. It can be reached by calling
roundEnv.getMessager()
Afterwards you can generate a new class and write it to the input of the compiler, as a .java file. This can be done by using the Filer.
An answer in StackOverflow really does no justice to this topic. I highly recommend looking at the Baeldung example and trying to uncover things from there. This API is as old as Java 6, but still not that greatly used. I encourage you, the reader, to try it out for yourself :)
看看https://github.com/rzwitserloot/lombok/,它添加了方法作为你描述的。
如
toString()
方法take a look at https://github.com/rzwitserloot/lombok/, It add methods as you described.
such as
toString()
methods base on the fields