在注释处理器中使用 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 命令行上工作):

  1. 正确的 module-info.java 设置与正确的 usesprovided with 注释处理器和服务的声明 正在加载
  2. 通过非默认ClassLoader加载ServiceLoader(参考bnorm的回答)
  3. 为要加载的服务提供META-INF/services文件
  4. 通过-classpath(或-processorpath)指定服务路径