为已编译的jar

时间:2015-11-17 23:35:47

标签: java api jar javassist

简介:

我有一个jar游戏是一个java游戏应用程序。我想创建一个mod加载器,用户可以使用它将代码加载到该jar中。 Mod只是代码片段,可以挂钩该jar中的特定方法来修改它的行为或增强它。在此阶段无法访问源代码。

我最初的想法是创建一个API,它将在执行jar最重要的方法时注入。用户/开发人员将能够针对该API(基本上是Java接口)进行编码。有很多字节码操作实用程序。我选择了javassist,它允许我在使用更高级别概念的方法之前/之后挂钩,而不仅仅是纯字节码。

经过一些工作后,我发现如果我可以自动执行前面描述的操作,它会更容易并为开发人员提供更多选项。简而言之:

1)程序将在jar中迭代类及其方法。

2)对于每个类,程序将使用模板引擎(例如freemarker)生成代码。这可能是为类中的每个方法提供方法之前和之后的一个接口,以及将保存实现的一个接口管理器

3)接口管理器将具有相同的方法并将被注入jar中。他的方法实现不会是任意的,它们只是接口的代理,因为接口实现可以在jar中动态加载和执行。

以下是一个例子:

假设有一个类,只有jar中的 getName()方法

生成器将在jar中迭代,对于该类和方法,它将创建代码

public interface ItemOverloader
{
   public void beforeGetName();
   public void afterGetName(); 
}

public ItemOverloadManager
{
  public static List<ItemOverloader> overloaders =  new ArrayList<>();

  public static void beforeGetName()
  {
     for(ItemOverloader i: overloaders)
        i.beforeGetName();
  }

  public static void afterGetName()
  {
     for(ItemOverloader i: overloaders)
        i.afterGetName();
  }
}

然后javassist库将:

CtClass itemClazz = ... bla bla
CtMethod getNameMethod = ... bla bla
getNameMethod.executeBefore("ItemOverloadManager.beforeGetName()");
getNameMethod.executeAfter("ItemOverloadManager.afterGetName()");

问题:

我担心如果这是自动执行的,它会在初始jar的每个方法中添加代码。虽然这可以成功地满足在每个方法中都有钩子的要求,但该代码会以两种方式引入性能开销:

1)每个方法调用都会查找一个静态类(管理器)和它的接口实现列表。

2)如果经理有实施,它会在列表中查找它们然后执行它们。

我想知道有多少性能开销,以及它是否是可接受的方式。如果没有,还有哪些其他方法可以自动生成对应用程序中大量方法调用的API访问?

如果所有这些挂钩都存在,但实现列表中存在的项目非常少,它是否仍会将性能降低相当数量?

1 个答案:

答案 0 :(得分:1)

不幸的是,性能开销是最好的测量而不是猜测。你当然应该使用你能找到的最好的算法并避免明显的愚蠢的事情(比如在紧密的循环中分配大块的内存,编写非常冗长和复杂的方法或使用过多/少数线程),但超出这个性能很难预料到。 Java通常非常擅长优化不必要的方法调用和没有可观察效果的代码,但确切的性能影响只能通过严格的测试来确定。

无论性能如何,我认为你提出的架构并不是一个好主意,感觉它会成为一个噩梦进行测试,很快就会导致类不变量被破坏。理想情况下,您应该非常仔细地规划代码的扩展点,而不是盲目地为每个方法生成它们。

明智的妥协可能是创建一个注释,您可以将其附加到要在API中公开的方法,并使annotation processor仅使用这些方法生成API。