-1

It seems reflection.proxy does not what is expected when there are overriden methods. In detail, starting with simple application:

static void debug( String fmt, Object... args ) {
    System.out.println( String.format(fmt,args));
}

interface I {
    void m1();
    void m2();
}

static class A implements I {
    public void m1() { System.out.println( "A.m1" ); m2(); }
    public void m2() { System.out.println( "A.m2" ); }
}

static class B extends A {
    @Override
    public void m2() { System.out.println( "B.m2" ); }
}


public static void main( String[] args )
{
    B b = new B();
    b.m1();
}

the output is, as expected:

A.m1
B.m2

Now, we try to proxify the calls to all methods of "B b". Following new code is added:

static public class HelloInvocationHandler implements InvocationHandler {

    I proxied;

    HelloInvocationHandler( I proxied ) {
        this.proxied = proxied;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {

        String methodName = method.getName();
        debug( "HelloInvocationHandler: invoke method %s", methodName);
        return method.invoke(proxied,args);
    }
}

public static void main( String[] args )
{
    B b = new B();
    HelloInvocationHandler handler = new HelloInvocationHandler(b);
    I pb = (I) Proxy.newProxyInstance(
            I.class.getClassLoader(),
            new Class[] { I.class },
            handler);

    pb.m1();
}

and the new output is:

HelloInvocationHandler: invoke method m1
A.m1
B.m2

as you can see, the call to "m2" is not executed accross the proxy. If all call to B's methods was accross the proxy, a line "HelloInvocationHandler: invoke method m2" should appear in the ouput.

Any hint?

Thanks.

pasaba por aqui
  • 3,446
  • 16
  • 40
  • I can't see it what is wrong with the second output. It's the same as the first plus an additional debug line :-/ – Aaron Digulla May 12 '15 at 10:10
  • Exactly, that seems correct, what were you expecting? – uraimo May 12 '15 at 10:11
  • If all call to B's methods was accross the proxy, a second line "HelloInvocationHandler: invoke method m2" should appear in the ouput. Clarified in the original post. – pasaba por aqui May 12 '15 at 10:12
  • Nope, `HelloInvocationHandler ` is calling it on the `proxied` instance, no additional proxy there. It's just a normal B.m2() method.invoke using the object you passed with the constructor. – uraimo May 12 '15 at 10:14
  • Yes, this is the problem. "method.invoke(proxy)" produces an infinite loop, "method.invoke(proxied)" doesn't traces the remainder execution. – pasaba por aqui May 12 '15 at 10:19

3 Answers3

3

You can use CGLib library to create proxy. Use Enhancer.create(B.class, new HelloInvocationHandler()) to to intercept method invocations. It's not harder than using JDK Proxy, but much more flexible. Should help in your case. The interceptor should be implemented like this:

public class HelloInvocationHandler implements MethodInterceptor {

    public Object intercept(Object object, Method method, Object[] args,
         MethodProxy methodProxy) throws Throwable {
     debug( "HelloInvocationHandler: invoke method %s", method.getName());
     return methodProxy.invokeSuper(object, args);
    }
}

Use it like this:

B pb = (B)Enhancer.create(B.class, new HelloInvocationHandler());
pb.m1();
Tagir Valeev
  • 97,161
  • 19
  • 222
  • 334
  • Hi. Thanks for your answer. Trying it, but statement "method.invokeSuper(object, args)" gives me an error of "unknown method". In addition, are you so kind of include the execution part (the call to a proxified B). Thanks a lot. – pasaba por aqui May 12 '15 at 10:57
  • Thanks a lot, now it does perfectly what was expected. – pasaba por aqui May 12 '15 at 12:09
2

Tagir got the solution, I have the explanation: The proxy doesn't "stick". Inside of method.invoke(proxied,args) control is given to the normal Java byte code. The variable this will now have the value proxied, so this.m2() will call the method from B.

There is no way using JDK Proxy to intercept all method calls inside of a class that you have built a proxy for. The reason for this is that Proxy is a hack: It just simulates what is necessary to invoke methods on the proxy. It doesn't actually change the code of the underlying Java classes. So when this is the proxy, the method calls will be routed through InvocationHandler.invoke(). As soon as the code leaves this method, normal Java rules apply.

To make it even easier to understand, your code above is equivalent to:

class HelloInvocationHandler implements I {
    I delegate;

    HelloInvocationHandler(I delegate ) {
        this.delegate = delegate;
    }

    public void m1() { delegate.m1(); }
    public void m2() { delegate.m2(); }
}

In this case, it's very easy to see why call to m2() inside of delegate.m1() isn't calling HelloInvocationHandler.m2().

Aaron Digulla
  • 321,842
  • 108
  • 597
  • 820
  • What I was expecting is that proxified class was equivalent to something like: class Proxified implements I { Override m1() { // call to handler }; Override m2() { // call to handler }; } – pasaba por aqui May 12 '15 at 11:04
  • It does that. What it doesn't do is changing the existing code for `A.m1()`. You really need to track what `this` is at all times. See my edits. – Aaron Digulla May 12 '15 at 11:21
0

It's correct, only pb is a Proxy class, the call to B.m2() is performed inside HelloInvocationHandler.invoke with a normal Method.invoke.

uraimo
  • 19,081
  • 8
  • 48
  • 55