如果使用带有自定义 Java 注释的 class,如何抛出注释处理错误
How to throw an annotation processing error if a class with a custom Java annotation is used
我想使用注解处理器创建 @Internal 注解,如果在库外使用内部库 class,它会抛出错误。
但是,我不确定如何找到引用 class 的位置然后抛出错误。
例如,使用这两个库 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
}
}
我该怎么做?
public class InternalProcessor extends AbstractProcessor {
@Override
public boolean process(final Set<? extends TypeElement> annotations,
final RoundEnvironment roundEnv) {
//What goes in here?
}
}
没有 hackery,这是相当困难的。您可以访问一些内容:
- 你得到 'mirror objects' 让你以编程方式查询正在编译的东西的签名。签名是 class 名称、它扩展的内容、字段、这些字段的类型、构造函数和方法,以及参数的类型和名称、它们的抛出子句、它们的 return 类型,以及任何签名中的任何注释。 但是您没有得到任何东西来检查该方法的主体。
- 您可以在编译时以原始字节的形式读取任意源文件。
- 您可以写出任意文件,包括新的源文件(然后在 'rounds' 系统中进行处理和编译)。值得注意的是,您无法修改现有文件中的代码。
- 您可以在任何签名或注释上发出错误和警告节点。
请注意您当时做起来并不容易:您想要访问您未获得的方法体。您唯一的选择是打开所有源文件,将它们扔进 java 解析器(java 不附带;您有多种选择,但它们都有缺点。javac
' s 是完整的和开源的,但有限制性许可,并不是那么容易遍历。他们还每隔一分钟更改 API。ecj
是完整的,许可许可的开源,而且速度快,有很多更稳定 API,但是 API 的设计很糟糕,文档也很糟糕。所有其他 java 解析器都是不完整的,因为没有严肃的项目使用它,因此你应该期待各种语言javac 和 ecj 可以处理的构造将使这样的解析器崩溃。它们也往往有错误的错误恢复,因此在源文件中的任何地方使用一个奇怪的或新的构造意味着整个文件都会为您出错并且没有您可以执行的分析。
让事情变得更复杂的是增量编译的概念。当您编辑一个文件并保存时,只有那个文件 会被重新编译,其余的不会被触及。这很重要:我可以修改源文件并开始使用其中带有“@Internal”标记的内容,然后是 运行 编译器(IDE 运行 它们在您保存时),以及 那个@Internal
注解不会触发任何东西因为没有代码被编译(相反,使用一个class如此标记的代码是正在编译)。无论注释如何,AP 都可以在任何编译时触发,因此在这方面您并非完全不走运,但您需要处理所有内容并扫描所有使用的类型。
因此,您想要做的是一个疯狂的复杂项目,专家 java 程序员至少需要一个多星期才能完成。您的工作需要:
- 立即接受大量编译速度下降;解析是任何编译的 90% 运行,你需要这样做 至少 两次,你可能需要接受更多的减速或构建一个复杂的缓存系统。
- 找出一个 java 解析器(java 是一种非常复杂的语言,因此这些解析器也是如此),以便按照您想要的方式分析代码。
- 然后在您编写了一个 'framework' 来解析和缓存 AST 以作为注释处理器飞过的所有内容后编写此项目。
退一步,只是阻止访问某些方法:模块系统,如 OSGi 和 java 的 module-info
也打算这样做,听起来你想彻底改造一些轮子。也许只是使用现有的模块系统?
我想使用注解处理器创建 @Internal 注解,如果在库外使用内部库 class,它会抛出错误。
但是,我不确定如何找到引用 class 的位置然后抛出错误。
例如,使用这两个库 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
}
}
我该怎么做?
public class InternalProcessor extends AbstractProcessor {
@Override
public boolean process(final Set<? extends TypeElement> annotations,
final RoundEnvironment roundEnv) {
//What goes in here?
}
}
没有 hackery,这是相当困难的。您可以访问一些内容:
- 你得到 'mirror objects' 让你以编程方式查询正在编译的东西的签名。签名是 class 名称、它扩展的内容、字段、这些字段的类型、构造函数和方法,以及参数的类型和名称、它们的抛出子句、它们的 return 类型,以及任何签名中的任何注释。 但是您没有得到任何东西来检查该方法的主体。
- 您可以在编译时以原始字节的形式读取任意源文件。
- 您可以写出任意文件,包括新的源文件(然后在 'rounds' 系统中进行处理和编译)。值得注意的是,您无法修改现有文件中的代码。
- 您可以在任何签名或注释上发出错误和警告节点。
请注意您当时做起来并不容易:您想要访问您未获得的方法体。您唯一的选择是打开所有源文件,将它们扔进 java 解析器(java 不附带;您有多种选择,但它们都有缺点。javac
' s 是完整的和开源的,但有限制性许可,并不是那么容易遍历。他们还每隔一分钟更改 API。ecj
是完整的,许可许可的开源,而且速度快,有很多更稳定 API,但是 API 的设计很糟糕,文档也很糟糕。所有其他 java 解析器都是不完整的,因为没有严肃的项目使用它,因此你应该期待各种语言javac 和 ecj 可以处理的构造将使这样的解析器崩溃。它们也往往有错误的错误恢复,因此在源文件中的任何地方使用一个奇怪的或新的构造意味着整个文件都会为您出错并且没有您可以执行的分析。
让事情变得更复杂的是增量编译的概念。当您编辑一个文件并保存时,只有那个文件 会被重新编译,其余的不会被触及。这很重要:我可以修改源文件并开始使用其中带有“@Internal”标记的内容,然后是 运行 编译器(IDE 运行 它们在您保存时),以及 那个@Internal
注解不会触发任何东西因为没有代码被编译(相反,使用一个class如此标记的代码是正在编译)。无论注释如何,AP 都可以在任何编译时触发,因此在这方面您并非完全不走运,但您需要处理所有内容并扫描所有使用的类型。
因此,您想要做的是一个疯狂的复杂项目,专家 java 程序员至少需要一个多星期才能完成。您的工作需要:
- 立即接受大量编译速度下降;解析是任何编译的 90% 运行,你需要这样做 至少 两次,你可能需要接受更多的减速或构建一个复杂的缓存系统。
- 找出一个 java 解析器(java 是一种非常复杂的语言,因此这些解析器也是如此),以便按照您想要的方式分析代码。
- 然后在您编写了一个 'framework' 来解析和缓存 AST 以作为注释处理器飞过的所有内容后编写此项目。
退一步,只是阻止访问某些方法:模块系统,如 OSGi 和 java 的 module-info
也打算这样做,听起来你想彻底改造一些轮子。也许只是使用现有的模块系统?