Reputation: 43
I have been using CGLIB's Enhancer for a while, but considering to switch over to Byte Buddy. It's pretty basic stuff, proxy for up to a few hundred data access interfaces, created on demand.
Enhancer enhancer = new Enhancer();
enhancer.setClassLoader(...);
enhancer.setSuperclass(...);
enhancer.setInterfaces(...);
enhancer.setCallback(...);
enhancer.create();
CGLIB is caching the generated type to improve performance. What is the recommended approach with Byte Buddy? I want to avoid any PermGen issues.
Upvotes: 4
Views: 1373
Reputation: 44032
UPDATE: Since version 1.6, Byte Buddy provides the TypeCache
class which uses soft or weak references as a blueprint for writing a cache with a custom key. This cache contains a callback method findOrInsert
which allows on demand creation of a type.
With Byte Buddy, you should write your own cache because it is you who knows best about:
cglib is keeping an internal cache in form of a static field with a synchronized map what bears some serious limitations. With this cache, it is not longer you who decides on the identity of a class when querying any Enhancer
instance when using the cache. Furthermore, the static field needs to keep track on the arguments for creating a class as for example the identity of the input callbacks which can be quite heavy. (As a matter of fact create memory leaks by itself.)
Byte Buddy wants to be an API for generating any Java class, not only for creating proxies. Therefore, you should know best what kind of caching makes sense. Considering your scenario where you only want to proxy an instance. Write a simple facade like:
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.
}
}
The advantage here is that you only need to keep track of the input class as a cache reference and you can avoid synchronization if your application was single threaded. Also, you could define your cache to be non-static if that would fit your use case better. And even better: You could use a real cache implementation. This way, every library can do what it can do best. Byte Buddy can create classes and the cache can, well, cache.
For full disclosure, I am the author of Byte Buddy and I decided on this approach after using cglib and javassist for a while. I have experienced several problems with their caches which is why I decided to not ship Byte Buddy with such a cache. I believe it is more of a convention which was introduced by the JDK proxy's implicit caching but I do not believe that these caches are generally a good idea for the reasons described above.
Upvotes: 4