Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javassist: how to create proxy of proxy?

Tags:

java

javassist

I'm creating proxies with javassist ProxyFactory. When creating a single proxy all works fine.

However, when I pass a proxied object to the proxying mechanism, it fails with

javassist.bytecode.DuplicateMemberException: duplicate method: setHandler in com.mypackage.Bean_$$_javassist_0_$$_javassist_1

I'm creating the proxies with this:

public Object createProxiedInstance(Object originalInstance) throws Exception {
    Class<?> originalClass = instance.getClass();
    ProxyFactory factory = new ProxyFactory();

    factory.setSuperclass(originalClass);

    factory.setHandler(new MethodHandler() {..});
    Class<T> proxyClass = factory.createClass();

    return proxyClass.newInstance();
}

So, how do I create proxies of proxies?

Update: The actual problems is that each proxy implements the ProxyObject which defines setHandler(..) method. So the 2nd proxy is trying to redefine the method, instead of overriding it in the subclass.

like image 268
Bozho Avatar asked Apr 11 '10 20:04

Bozho


2 Answers

The problem was (actually, it's the same with CGLIB - I tried it using commons-proxy) that I should not try to create a proxy class of the proxy class. The second proxy should again be of the original class. So adding the following line resolves the problem:

if (instance instanceof ProxyObject) {
    originalClass = originalClass.getSuperclass();
}

And an advice - if you can use some sort of interceptors (like the ones defined in commons-proxy), do it instead of using multiple proxies.

like image 52
Bozho Avatar answered Sep 19 '22 15:09

Bozho


Its a rather late answer but you might still be interested in knowing this:

Javassist proxies are implemented rather naively. In your above code, Javassist will always create a proxy class with the following methods:

  1. A method for any overridable method of the base class
  2. Two methods to (a) get a proxy handler (getHandler) and (b) set a proxy handler (setHandler)

The names of the two latter methods are hardcoded by Javassist and represented by the ProxyObject interface. If you now create a proxy class of a proxy class, Javassist would schedule the creation of ProxyObject's methods twice. Once by the first condition and once by the second condition.

You could avoid this by setting a MethodFilter which specifies to not override the ProxyObject's methods such that javassist would only create the methods by the second condition. However, this would imply that you could not longer set a ProxyObject for the super class proxy without directly accessing the corresponding field via reflection. Therefore, your approach is probably the cleanest.

cglib defines callbacks per class and not per instance such that this problem with cglib is slightly different but results in another conflict.

However, if you want to create proxy classes that do not suffer these shortcomings, you might be interested in my library Byte Buddy which I wrote after getting frustrated working with cglib and javassist when working in corner cases. If you are working with runtime code generation I hope that it might help offer you some flexibility that the other libraries lack.

like image 38
Rafael Winterhalter Avatar answered Sep 18 '22 15:09

Rafael Winterhalter