我一直在使用CGLIB的Enhancer,但考虑切换到Byte Buddy。它是非常基本的东西,可以根据需要代理多达数百个数据访问接口。
Enhancer enhancer = new Enhancer();
enhancer.setClassLoader(...);
enhancer.setSuperclass(...);
enhancer.setInterfaces(...);
enhancer.setCallback(...);
enhancer.create();
CGLIB正在缓存生成的类型以提高性能。 Byte Buddy的推荐方法是什么?我想避免任何PermGen问题。
答案 0 :(得分:3)
更新:从版本1.6开始,Byte Buddy提供TypeCache
类,它使用软引用或弱引用作为使用自定义键编写缓存的蓝图。此缓存包含一个回调方法findOrInsert
,允许按需创建类型。
对于Byte Buddy,您应该编写自己的缓存,因为您最了解的是:
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代理隐式缓存引入的约定,但我不相信这些缓存通常是出于上述原因的好主意。