3

我一直在使用 CGLIB 的 Enhancer,但正在考虑切换到 Byte Buddy。这是非常基本的东西,可以代理多达数百个数据访问接口,按需创建。

Enhancer enhancer = new Enhancer();
enhancer.setClassLoader(...);
enhancer.setSuperclass(...);
enhancer.setInterfaces(...);
enhancer.setCallback(...);
enhancer.create();

CGLIB 正在缓存生成的类型以提高性能。Byte Buddy 的推荐方法是什么?我想避免任何 PermGen 问题。

4

1 回答 1

3

更新:从 1.6 版开始,Byte Buddy 提供了TypeCache使用软引用或弱引用作为使用自定义键编写缓存的蓝图的类。此缓存包含findOrInsert允许按需创建类型的回调方法。

使用 Byte Buddy,您应该编写自己的缓存,因为您最了解:

  • 什么标准对缓存有意义 - 也许您需要两个具有不同身份的相似类?Byte Buddy 不应该为您做出决定。
  • 您需要保留哪些信息来维护缓存?Cglib 需要跟踪所有可以防止垃圾收集的输入信息,即使对于使用“短期”类加载器加载的类也是如此。

cglib 以带有同步映射的静态字段的形式保持内部缓存,这具有一些严重的限制。使用此缓存,在使用缓存时查询任何 Enhancer实例时,不再由您决定类的身份。此外,静态字段需要跟踪创建类的参数,例如输入回调的标识,这可能非常繁重。(事实上​​,它本身会造成内存泄漏。)

Byte Buddy 希望成为生成任何 Java 类的 API,而不仅仅是创建代理。因此,您应该最清楚哪种缓存是有意义的。考虑您只想代理实例的场景。写一个简单的外观,如:

class MyProxyGenerator {
  static Map<Class<?>, Class<?>> proxies = new HashMap<>();
  public Class<?> makeProxy(Class<?> type) {
    if(proxies.contains(type)) {
      return proxies.get(type);
    } else {
      Class<?> proxy = doMakeProxy(type);
      proxies.put(type, proxy);
      return proxy;
    }
  }
  private Class<?> doMakeProxy(Class<?> type) {
    // use Byte Buddy here.
  }
}

这样做的好处是您只需要跟踪输入类作为缓存引用,并且如果您的应用程序是单线程的,您可以避免同步。此外,如果更适合您的用例,您可以将缓存定义为非静态的。甚至更好:您可以使用真正的缓存实现。这样,每个图书馆都可以做它最擅长的事情。Byte Buddy 可以创建类,而缓存也可以缓存。

为了全面披露,我是 Byte Buddy 的作者,我在使用 cglib 和 javassist 一段时间后决定采用这种方法。我在他们的缓存中遇到了几个问题,这就是为什么我决定不向 Byte Buddy 发送这样的缓存。我相信这更像是 JDK 代理的隐式缓存引入的约定,但由于上述原因,我认为这些缓存通常不是一个好主意。

于 2014-05-19T08:48:28.920 回答