reflection.proxy 覆盖时无效

reflection.proxy not valid when override

似乎 reflection.proxy 在存在重写方法时并没有达到预期效果。详细的,从简单的应用开始:

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();
}

输出如预期的那样:

A.m1
B.m2

现在,我们尝试代理对"B b"所有方法的调用。添加了以下新代码:

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();
}

新的输出是:

HelloInvocationHandler: invoke method m1
A.m1
B.m2

如您所见,对 "m2" 的调用并未跨代理执行。如果对 B 方法的所有调用都是通过代理进行的,则输出中应出现一行 "HelloInvocationHandler: invoke method m2"。

有什么提示吗?

谢谢。

您可以使用 CGLib 库来创建代理。使用 Enhancer.create(B.class, new HelloInvocationHandler()) 来拦截方法调用。它并不比使用 JDK Proxy 难,但更灵活。应该对你的情况有帮助。拦截器应该这样实现:

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);
    }
}

这样使用:

B pb = (B)Enhancer.create(B.class, new HelloInvocationHandler());
pb.m1();

没错,只有pb是一个Proxy class,对B.m2()的调用是在HelloInvocationHandler.invoke里面用正常的Method.invoke调用的。

Tagir 得到了解决方案,我有解释:代理没有 "stick"。 method.invoke(proxied,args) 内部的控制权交给了正常的 Java 字节码。变量 this 现在的值为 proxied,因此 this.m2() 将从 B.

调用方法

无法使用 JDK Proxy 拦截您为其构建代理的 class 内的所有方法调用。这样做的原因是 Proxy 是一个 hack:它只是模拟调用代理上的方法所必需的。它实际上并没有改变底层 Java classes 的代码。所以当 this 是代理时,方法调用将通过 InvocationHandler.invoke() 路由。一旦代码离开此方法,就会应用正常的 Java 规则。

为了更容易理解,您上面的代码等同于:

class HelloInvocationHandler implements I {
    I delegate;

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

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

在这种情况下,很容易看出为什么在 delegate.m1() 内部调用 m2() 没有调用 HelloInvocationHandler.m2().