java 使用注释处理器替换代码

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13690272/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 13:38:08  来源:igfitidea点击:

Code replacement with an annotation processor

javacode-generationannotation-processing

提问by Boann

I'm trying to write an annotation processorto insert methods and fields on a class... and the documentation is so sparse. I'm not getting far and I don't know if I'm approaching it correctly.

我正在尝试编写一个注释处理器来在类上插入方法和字段......而且文档非常稀疏。我没有走远,我不知道我是否正确地接近它。

The processing environment provides a Filerobject which has handy methods for creating new source and class files. Those work fine but then I tried to figure out how read the existing source files, and all it provides is "getResource". So in my Processor implementation I've done this:

处理环境提供了一个Filer对象,该对象具有用于创建新源文件和类文件的便捷方法。这些工作正常,但后来我试图弄清楚如何读取现有的源文件,它提供的只是“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;
}

My first quandary is I can't help feeling that element.asType().toString().replace(".", "/") + ".java"(to get the qualified type name and convert it into a package and source file path) is not a nice way to approach the problem. The rest of the API is so over-engineered but there doesn't seem to be a handy method for retrieving the original source code.

我的第一个困惑是我不禁觉得element.asType().toString().replace(".", "/") + ".java"(获取限定类型名称并将其转换为包和源文件路径)不是解决问题的好方法。API 的其余部分被过度设计,但似乎没有一种方便的方法来检索原始源代码。

The real problem is that then the compiler gets spontaneously upset by the second source file in the output directory ("error: duplicate class") and now I'm stuck.

真正的问题是,编译器会自发地被输出目录中的第二个源文件(“错误:重复类”)打乱,现在我被卡住了。

I've already written the rest of this -- a macro lexer and parser and whatnot for calculating some data and inserting the field values and methods -- but it operates as a initial step outside the compiler. Except for the fact that the original files cannot have a .java extension (to prevent the compiler seeing them), this works nicely. Then I heard that annotations can do code generation, which I assume will be more proper and convenient, but I can't find much guidance on it.

我已经写了剩下的部分——一个宏词法分析器和解析器,以及用于计算一些数据和插入字段值和方法的东西——但它作为编译器之外的初始步骤运行。除了原始文件不能有 .java 扩展名(以防止编译器看到它们)之外,这很好用。然后我听说注解可以做代码生成,我认为这会更合适和方便,但我找不到太多关于它的指导。

采纳答案by John Ericksen

The intention behind the annotation processor is to allow a developer to add new classes, not replace existing classes. That being said, there is a bug that allows you to add code to existing classes. Project Lombokhas leveragedthis to add getter and setter (among other things) to your compiled java classes.

注释处理器背后的意图是允许开发人员添加新类,而不是替换现有类。话虽如此,但存在一个允许您向现有类添加代码的错误。 龙目岛项目利用这getter和setter(除其他事项外)添加到您的编译的Java类。

The approach I have taken to 'replace' methods/fields is either extend from or delegate to the input class. This allows you to override/divert calls to the target class.

我用来“替换”方法/字段的方法是从输入类扩展或委托给输入类。这允许您覆盖/转移对目标类的调用。

So if this is your input class:

因此,如果这是您的输入类:

InputImpl.java:

InputImpl.java:

public class InputImpl implmements Input{
    public void foo(){
        System.out.println("foo");
    }
    public void bar(){
        System.out.println("bar");
    }
}

You could generate the following to "replace" it:

您可以生成以下内容来“替换”它:

InputReplacementImpl.java:

InputReplacementImpl.java:

public class InputReplacementImpl implmements Input{

    private Input delegate;

    //setup delegate....

    public void foo(){
        System.out.println("foo replacement");
    }
    public void bar(){
        delegate.bar();
    }
}

This begs the question, how do you reference InputReplacementImplinstead of InputImpl. You can either generate some more code to perform the wrapping or simply call the constructor of the code expected to be generated.

这就引出了一个问题,你如何引用InputReplacementImpl而不是InputImpl. 您可以生成更多代码来执行包装,也可以简单地调用预期生成的代码的构造函数。

I'm not really sure what your question is, but I hope this sheds some light on your issues.

我不太确定你的问题是什么,但我希望这能对你的问题有所了解。

回答by Jonas Bergstr?m

A bit late :), but one solution could be to use Byte Buddy's transformer as part of the build process. See eg https://github.com/raphw/byte-buddy/tree/master/byte-buddy-maven-plugin.

有点晚了 :),但一种解决方案可能是使用 Byte Buddy 的转换器作为构建过程的一部分。参见例如https://github.com/raphw/byte-buddy/tree/master/byte-buddy-maven-plugin