更新:从 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 代理的隐式缓存引入的约定,但由于上述原因,我认为这些缓存通常不是一个好主意。