[英]Code replacement with an annotation processor
我正在尝试编写一个注释处理器以在 class 上插入方法和字段......而且文档非常稀疏。 我没有走远,我不知道我是否正在正确地接近它。
处理环境提供了一个Filer
object,它具有创建新源和 class 文件的便捷方法。 这些工作正常,但后来我试图弄清楚如何读取现有的源文件,它提供的只是“getResource”。 所以在我的处理器实现中,我这样做了:
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
try {
for (TypeElement te : annotations) {
for (Element element : roundEnv.getElementsAnnotatedWith(te)) {
FileObject in_file = processingEnv.getFiler().getResource(
StandardLocation.SOURCE_PATH, "",
element.asType().toString().replace(".", "/") + ".java");
FileObject out_file = processingEnv.getFiler().getResource(
StandardLocation.SOURCE_OUTPUT, "",
element.asType().toString().replace(".", "/") + ".java");
//if (out_file.getLastModified() >= in_file.getLastModified()) continue;
CharSequence data = in_file.getCharContent(false);
data = transform(data); // run the macro processor
JavaFileObject out_file2 = processingEnv.getFiler().createSourceFile(
element.asType().toString(), element);
Writer w = out_file2.openWriter();
w.append(data);
w.close();
}
}
} catch (Exception e) {
e.printStackTrace();
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, e.getMessage());
}
return true;
}
我的第一个窘境是我忍不住觉得element.asType().toString().replace(".", "/") + ".java"
(获取限定类型名称并将其转换为 package 和源文件路径)不是解决问题的好方法。 API 的 rest 设计过度,但似乎没有检索原始源代码的便捷方法。
真正的问题是编译器会自发地被 output 目录中的第二个源文件打乱(“错误:重复类”),现在我被卡住了。
我已经编写了 rest 这个——一个宏词法分析器和解析器以及诸如此类的东西,用于计算一些数据和插入字段值和方法——但它作为编译器之外的初始步骤运行。 除了原始文件不能有 .java 扩展名(以防止编译器看到它们)这一事实外,这很好用。 然后我听说注解可以生成代码,我觉得这样更合适也更方便,但是我找不到太多的指导。
注释处理器背后的意图是允许开发人员添加新类,而不是替换现有类。 话虽如此,但存在一个允许您向现有类添加代码的错误。 龙目岛项目利用这一点将 getter 和 setter(以及其他东西)添加到您编译的 java 类中。
我用来“替换”方法/字段的方法是从输入类扩展或委托给输入类。 这允许您覆盖/转移对目标类的调用。
因此,如果这是您的输入类:
InputImpl.java:
public class InputImpl implements Input{
public void foo(){
System.out.println("foo");
}
public void bar(){
System.out.println("bar");
}
}
您可以生成以下内容来“替换”它:
InputReplacementImpl.java:
public class InputReplacementImpl implements Input{
private Input delegate;
//setup delegate....
public void foo(){
System.out.println("foo replacement");
}
public void bar(){
delegate.bar();
}
}
这就引出了一个问题,你如何引用InputReplacementImpl
而不是InputImpl
。 您可以生成更多代码来执行包装,也可以简单地调用预期生成的代码的构造函数。
我不太确定你的问题是什么,但我希望这能对你的问题有所了解。
有点晚了 :),但一种解决方案可能是使用 Byte Buddy 的转换器作为构建过程的一部分。 参见例如https://github.com/raphw/byte-buddy/tree/master/byte-buddy-maven-plugin 。
我不知道这个提示是否符合您的需求,但您的想法将与依赖注入结合使用,例如 Spring。
相反,Lombok 是直接操作抽象源代码树,这比生成源代码要复杂得多。 这里的问题是,Java-Compiler-Module 暴露了(Jigsaw)。
另一种选择是使用像 ByteBuddy 这样的字节码 API,在运行时代理原始的 class。 如果您不想为实例化 class 编写代码,这也需要依赖注入。这里的魔法是配置 class 中类型集合的自动装配字段。如果您对此感兴趣,请告诉我。 然后我将在此处添加更多详细信息。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.