首页 > 解决方案 > 如何使用 javax.tools.JavaCompiler 创建一个调用自定义注释过滤的子类方法列表的函数?

问题描述

我有这种不寻常的(我认为?)情况

public abstract class Base {

   public Base() {
       // code
   }

   public final void executeAll() {
      final Foo foo = new Foo();
      final Bar bar = new Bar();
      // now execute all method of "this" that: 
      //   - have annotation mark
      //   - have return type void
      //   - accept (foo, bar) has parameters
      //   - name can be whatever you want
      //   - protected
   }

   @Retention(RetentionPolicy.RUNTIME)
   @Target({ElementType.METHOD})
   protected @interface Mark{
   }
}

现在我有一个扩展 Base 的类,并有一些带有签名的方法,如 executeAll 方法中所述,如下所示:

public class Test extends Base {

   @Mark
   protected void willBeCalled(Foo a, Bar b) {
   }

   @Mark
   protected void alsoThisOne(Foo a, Bar b) {
   }

   @Mark
   protected void yep(Foo a, Bar b) {
   }

}

我知道这可以很容易地实现,我创建一个“受保护的摘要Collection<BiConsumer<Foo, Bar>> getToBeCalled()”让每个孩子都扩展它并将 executeAll() 方法编写为:

   protected abstract Collection<BiConsumer<Foo, Bar>> getToBeCalled();

   public final void executeAll() {
      final Foo foo = new Foo();
      final Bar bar = new Bar();
      getToBeCalled().stream.forEach(bic-> bic.accept(foo, bar));
   }

但这不是我想要达到的。我需要一种方法来从子类中执行方法,而无需开发人员进行任何重大干预(这就是为什么我考虑使用注释来标记希望执行的方法,仅此而已)。

现在我有一个工作实现,在 Base 空构造函数中使用 扫描每个可用的方法this.getClass().getMethods(),过滤它们(必须有 Mark 注释,按顺序只接受 Foo 参数和 Bar 参数并返回 void,受保护)并将它们保存在aprivate List<Method> methods;后来被 executeAll 用来调用每个方法。

最近我也读到了这个:https ://www.optaplanner.org/blog/2018/01/09/JavaReflectionButMuchFaster.html ...

...并且只是为了好玩实现了一个 MethodHandle 版本(我认为在我的用例中可能会更快),并计划制作一个 LambdaMetafactory 版本...但我真的对 javax.tools.JavaCompiler 很感兴趣解决方案。我仍在尝试弄清楚如何做到这一点...我保留了我原来的方法名称搜索和过滤,我只保留了名称...现在呢?如何将方法名称列表(我知道我可以访问,因为我从“this”调用并在子类中声明为受保护,并且还具有特定签名)转换为可用的东西,比如单个private final BiConsumer<Foo, Bar> callAll;由 Base() 构造函数计算和保存,并由 executeAll 函数调用,如下所示:

public abstract class Base {

   private final BiConsumer<Foo, Bar> callAll;

   protected Base() {
       // get all methods <<< already done
       Method[] methods = ;
       // filter them by annotation, visibility and signature <<< already done
       List<Method> filtered> = ;
       // save only the names <<< already done
       List<String> names = ;
       // use javax.tools.JavaCompiler somehow <<< I miss this
       callAll = ???; 
   }

   public final void executeAll() {
      final Foo foo = new Foo();
      final Bar bar = new Bar();
      callAll.accept(foo, bar);
   }

   @Retention(RetentionPolicy.RUNTIME)
   @Target({ElementType.METHOD})
   protected @interface Mark{
   }
}

当然,使用 BiConsumer 作为 callAll 的想法只是一个想法,如果有更好的东西我完全可以接受建议。

标签: javajava-compiler-apijavacompiler

解决方案


推荐阅读