如果使用带有自定义 Java 注释的类,如何引发注释处理错误

发布于 2025-01-15 09:57:48 字数 1044 浏览 2 评论 0原文

我想使用注释处理器创建 @Internal 注释,如果在库外部使用内部库类,该注释会引发错误。

但是,我不确定如何找到引用类的位置并抛出错误。

例如,对于这两个库类:

//Implementation
@Internal
public class InternalClass implements SomeInterface {
}
//API class
public interface SomeInterface {

     static SomeInterface getInstance() { 
        return new InternalClass();
     }
}
public class Test {
   public static void main(String[] args) {
      InternalClass annotated = new InternalClass(); //ERROR
      SomeInterface interface = SomeInterface.getInstance(); //FINE
    }
}

我该怎么做?

public class InternalProcessor extends AbstractProcessor {

    @Override
    public boolean process(final Set<? extends TypeElement> annotations,
                           final RoundEnvironment roundEnv) {
        //What goes in here?
    }
}

I want to use an annotation processor to create an @Internal annotation which throws an error if an internal library class is used outside the library.

However, I'm not sure how to find where a class is referenced and throw an error then.

For example, with these 2 library classes:

//Implementation
@Internal
public class InternalClass implements SomeInterface {
}
//API class
public interface SomeInterface {

     static SomeInterface getInstance() { 
        return new InternalClass();
     }
}
public class Test {
   public static void main(String[] args) {
      InternalClass annotated = new InternalClass(); //ERROR
      SomeInterface interface = SomeInterface.getInstance(); //FINE
    }
}

How would I do that?

public class InternalProcessor extends AbstractProcessor {

    @Override
    public boolean process(final Set<? extends TypeElement> annotations,
                           final RoundEnvironment roundEnv) {
        //What goes in here?
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

花辞树 2025-01-22 09:57:48

如果没有黑客技术,这是相当困难的。您可以访问一些东西:

  • 您可以获得“镜像对象”,它允许您以编程方式查询正在编译的内容的签名。签名是类名、它扩展的内容、字段、这些字段的类型、构造函数和方法、参数的类型和名称、它们的 throws 子句、它们的返回类型以及任何签名中的任何注释。 但是你没有得到任何东西来检查该方法的主体
  • 您可以在编译时以原始字节的形式读取任意源文件。
  • 您可以写出任意文件,包括新的源文件(然后在“轮次”系统中进行处理和编译)。值得注意的是,您无法修改现有文件中的代码。
  • 您能够在任何签名或注释上发出错误和警告节点。

请注意,您无法轻松做到这一点:您想要访问您无法获得的方法体。您唯一的选择是打开所有源文件,将它们放入 java 解析器(java 不附带该解析器;您有多种选择,但它们都有缺点。javac 是完整且开放的源代码,但受到限制性许可,并且并非所有内容都可以轻松遍历。ecj 每隔一分钟就会更改一次 API,它是完整的、许可的开源代码,而且速度很快,而且 API 更加稳定。 API 的设计很糟糕所有其他 java 解析器都是不完整的,从某种意义上说,没有严肃的项目使用它,因此您应该预料到 javac 和 ecj 可以处理的各种语言结构都会使这样的解析器崩溃,因此它们也往往具有糟糕的错误恢复能力。在源文件中的任何地方使用奇怪的或新的构造意味着整个文件都会出错,并且您无法执行分析,

当您编辑文件并保存时,会出现增量编译的概念。 就是这样文件被重新编译,其余部分不变。这是一件大事:我可以修改源文件并开始使用其中的“@Internal”标记的内容,然后运行编译器(IDE 在您保存时运行它们),然后那个@Internal 注释不会触发任何内容,因为没有包含该注释的代码正在被编译(相反,使用这样标记的类的代码正在被编译)。无论注释如何,AP 都可以在任何编译上触发,因此您在这方面并非完全不走运,但您需要处理所有内容并扫描所有使用的类型。

因此,您要做的是一个疯狂复杂的项目,一个专业的 java 程序员至少需要一周以上的时间才能完成。你的工作需要:

  • 立即接受编译速度的大幅减慢;解析占任何编译运行的 90%,您需要至少执行两次,并且您可能需要接受更多的减速或构建复杂的缓存系统。
  • 找出一个java解析器(java是一种非常复杂的语言,因此这些解析器也是如此),以便按照您想要的方式分析代码。
  • 一旦你编写了一个“框架”来解析和缓存你所看到的作为注释处理器飞过的所有内容的 AST,然后就可以编写这个项目。

退后一步,只是阻止访问某些方法:诸如 OSGi 和 java 的 module-info 之类的模块系统也旨在做到这一点,听起来您想重新发明一些轮子。也许只使用现有的模块系统?

Without hackery, this is rather difficult. You get access to a few things:

  • You get 'mirror objects' that let you programmatically query signatures of stuff being compiled. Signatures is the class name, what it extends, the fields, the types of those fields, the constructors and the methods, and the types and names of the parameters, their throws clauses, their return types, and any annotations in any signature. But you do not get anything to inspect the body of that method.
  • You get the ability to read arbitrary source files under compilation, as raw bytes.
  • You get the ability to write arbitrary files out, including new source files (which then get processed and compiles in a 'rounds' system). Notably you don't get the ability to modify code in existing files.
  • You get the ability to emit error and warning nodes on any signature or annotation.

Note how you just can't do it easily then: You want access to method bodies which you don't get. Your only option is to open all the source files, throw them through a java parser (which java doesn't ship with; you have various options but they all have downsides. javac's is complete and open source but restrictively licensed and not all that easily traversed. They also change that API every other minute. ecj's is complete, permissively licensed open source, and fast, with a much more stable API, but that API is deplorably designed and badly documented. All other java parsers are incomplete, in the sense that no serious project uses it and thus you should expect that various language constructs that javac and ecj can handle will crash such a parser. They also tend to have bad error recovery so one weird or new construct used anywhere in a source file means that entire file just errors out for you and there's no analysis you can perform.

To make matters even more complicated, there's the notion of incremental compilation. When you edit a file, and save, Just that file gets recompiled, the rest isn't touched. This is a big deal: I can modify a source file and start using '@Internal' marked stuff in it, and then run compilers (IDEs run them as you save), and that @Internal annotation does not trigger anything because no code with it is being compiled (instead, code that uses a class so marked is being compiled). APs can just trigger on any compilation regardless of annotations, so you're not completely out of luck on this regard, but you need to process everything and scan all used types.

What you want to do is therefore a crazy complicated project that an expert java programmer will need more than a week to put together at least. Your job would entail:

  • Immediately accepting massive slowdowns in compilation; parsing is 90% of any compile run and you need to do so at least twice, and you probably need to either accept even more slowdowns or build a complicated caching system.
  • Figuring out a java parser (java is a very complicated language, hence so are these parsers), in order to analyse the code the way you want to.
  • Then write this project once you've written a 'framework' that parses and caches the ASTs for everything that you see fly on by as an annotation processor.

Taking a step back, just preventing access to certain methods: Module systems such as OSGi and java's module-info aim to do that too, it sounds like you want to badly reinvent some wheels. Maybe just use an existing module system?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文