在注释处理器中使用 ServiceLoader
Using ServiceLoader within an annotation processor
是否可以在注释处理器的 init(ProcessingEnvironment)
方法中使用 ServiceLoader
?
interface Service {}
class AnnotationProcessor extends AbstractProcessor {
public static void main(String[] args) {
ServiceLoader<Service> loader = ServiceLoader.load(Service.class);
System.out.println("Found Services:");
for (Service service : loader) {
System.out.println(service);
}
}
@Override
public synchronized void init(ProcessingEnvironment env) {
super.init(env);
ServiceLoader<Service> loader = ServiceLoader.load(Service.class);
System.out.println("Found Services:");
for (Service service : loader) {
System.out.println(service);
}
}
...
}
运行 main 方法生成我在 META-INF/services 文件中指定的服务。但是,当 init(ProcessingEnvironment)
方法作为另一个项目构建的一部分被调用时,它不会列出任何服务。
有没有办法让它工作?
问题是 ServiceLoader 在未指定类加载器时使用 Thread.currentThread().getContextClassLoader()
,它无法从注释处理器中看到 META-INF\services
文件,但可以从 main
方法中看到。
使用 ServiceLoader.load(Service.class, AnnotationProcessor.class.getClassLoader())
从 AnnotationProcessor 中正确加载服务。
(如果您知道为什么 ContextClassLoader 看不到 META-INF\services
,请随时添加到我的回答中)
如果您打算 运行 您的注释处理器带有 Java 模块系统:
在我看来,对于 Java 11,javac
编译器并不完全支持模块。我成功地使用 --module-path
正确设置了 javac
以进行注释处理,但事实证明无法通过 ServiceLoader
以相同的方式将插件加载到我的注释处理器中。因此,在花了很长时间摆弄所有可能的编译器选项之后,我最终以混合方式通过模块路径加载处理器并通过 class 路径加载服务。
总而言之,我需要执行以下步骤(我直接在 javac
命令行上工作):
- 正确的
module-info.java
设置与正确的 uses
和 provided with
注释处理器和服务的声明
正在加载
- 通过非默认
ClassLoader
加载ServiceLoader
(参考bnorm的回答)
- 为要加载的服务提供
META-INF/services
文件
- 通过
-classpath
(或-processorpath
)指定服务路径
是否可以在注释处理器的 init(ProcessingEnvironment)
方法中使用 ServiceLoader
?
interface Service {}
class AnnotationProcessor extends AbstractProcessor {
public static void main(String[] args) {
ServiceLoader<Service> loader = ServiceLoader.load(Service.class);
System.out.println("Found Services:");
for (Service service : loader) {
System.out.println(service);
}
}
@Override
public synchronized void init(ProcessingEnvironment env) {
super.init(env);
ServiceLoader<Service> loader = ServiceLoader.load(Service.class);
System.out.println("Found Services:");
for (Service service : loader) {
System.out.println(service);
}
}
...
}
运行 main 方法生成我在 META-INF/services 文件中指定的服务。但是,当 init(ProcessingEnvironment)
方法作为另一个项目构建的一部分被调用时,它不会列出任何服务。
有没有办法让它工作?
问题是 ServiceLoader 在未指定类加载器时使用 Thread.currentThread().getContextClassLoader()
,它无法从注释处理器中看到 META-INF\services
文件,但可以从 main
方法中看到。
使用 ServiceLoader.load(Service.class, AnnotationProcessor.class.getClassLoader())
从 AnnotationProcessor 中正确加载服务。
(如果您知道为什么 ContextClassLoader 看不到 META-INF\services
,请随时添加到我的回答中)
如果您打算 运行 您的注释处理器带有 Java 模块系统:
在我看来,对于 Java 11,javac
编译器并不完全支持模块。我成功地使用 --module-path
正确设置了 javac
以进行注释处理,但事实证明无法通过 ServiceLoader
以相同的方式将插件加载到我的注释处理器中。因此,在花了很长时间摆弄所有可能的编译器选项之后,我最终以混合方式通过模块路径加载处理器并通过 class 路径加载服务。
总而言之,我需要执行以下步骤(我直接在 javac
命令行上工作):
- 正确的
module-info.java
设置与正确的uses
和provided with
注释处理器和服务的声明 正在加载 - 通过非默认
ClassLoader
加载ServiceLoader
(参考bnorm的回答) - 为要加载的服务提供
META-INF/services
文件 - 通过
-classpath
(或-processorpath
)指定服务路径