JDK动态代理的学习与理解之:invoke方法为什么不能method.invoke(proxy,args[])

1 篇文章 0 订阅
1 篇文章 0 订阅

在学习动态代理时,始终让我迷茫的一个地方是,InvocationHandler的invoke方法中的第一个参数(Object proxy)是什么作用,从字面上来说是代理对象,那么mehtod.invoke(proxy,args)会造成stackOverflow呢?

/**
 * 一个代理实现
 */
 /**
 * 模拟一个代理实现
 */
public class ProxyTest3{

    public interface Interface {
        void print();
    }
    public class A implements Interface {
        @Override
        public void print() {
            System.out.print("AAAAAAAAAA");
        }
    }
    public class B implements Interface {
        @Override
        public void print() {
            System.out.print("BBBBBBBB");
        }
    }

    public <T>Object createProxy(Class<T> cla) {
        return (T)Proxy.newProxyInstance(cla.getClassLoader(), cla.getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //method.invoke(proxy,args);
                return null;
            }
        });
    }

    public static void main(String[] args) throws IOException {
        ProxyTest3 p = new ProxyTest3();
        Interface i = (Interface) p.createProxy(A.class);
        //会死循环
        i.print();
    }
}


执行上线的代码会出现代码执行结果
那么是为什么会出现这种情况呢?

答案在代理类中,我们上述的代码 Interface i = (Interface) p.createProxy(A.class); 此处的 i 获取到的其实是一个extends Proxy implements Interface的代理类(代理的核心实现就是通过目标对象类的字节码来重新生成一个类对象),生成的代理类如下:

//此处类的结构就是extends Proxy implements 指定接口
//从这个地方也能 明白为什么jdk代理只能代理接口(因为Java是单继承,而jdkProxy生成的代理类对象必然会继承Proxy,因此,只能代理接口)
//不过如果是一个无实现接口无继承类的类,调用Proxy.newProxyInstance,会生成一个extends Proxy implements 自身的一个代理类,但在实际的使用中,这样的类是无法转换成我们想要的东西的,且也无法使用(没发进行方法调用的)
public final class class extends Proxy implements Interface {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

    public class(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void print() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("com.ydj.netty.proxy.ProxyTest3$Interface").getMethod("print");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

看到这个地方,问题就迎刃而解了,`

        /**
             * 为什么method.invoke(第一个参数不能是入参的proxy)
             * 首先proxy是代理类的对象,代理类中的方法的实现是
             *     public final void printaa(String var1) throws  {
             *         try {
             *             super.h.invoke(this, m3, new Object[]{var1});
             *         } catch (RuntimeException | Error var3) {
             *             throw var3;
             *         } catch (Throwable var4) {
             *             throw new UndeclaredThrowableException(var4);
             *         }
             *     }
             *   此处的method是接口的抽象方法,如果实现类为代理类的话
             *   比如此时的方法名是:printaa,proxy.printaa的实现是是h.invoke,然后h.invoke又进来,就会实死循环
             *         i.printaa -> $Proxy0.printaa()->super.h.invoke(this, m3, null)
    m3=Class.forName("org.example.ela.ProxyTest3$Interface").getMethod("printaa", Class.forName("java.lang.String"));
    那么super.h.invoke(this,m3,null) => 去调用了InterFace.printaa() 即 i.printaa() 从而形成了死循环
             */`
  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
JDK动态代理和CGLib动态代理都是实现AOP编程的方式,它们的使用上有以下异同点: 1. JDK动态代理只能代理实现了接口的类,而CGLib动态代理可以代理没有实现接口的类。 2. JDK动态代理是通过反射来实现的,而CGLib动态代理使用的是继承。 3. JDK动态代理在生成代理对象时,需要传入一个InvocationHandler对象,而CGLib动态代理在生成代理对象时,需要继承MethodInterceptor类,并重写intercept()方法。 4. JDK动态代理生成的代理对象性能比CGLib动态代理生成的代理对象性能要低。 下面是一个JDK动态代理的例子: ```python import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; interface Subject { void request(); } class RealSubject implements Subject { public void request() { System.out.println("RealSubject request"); } } class DynamicProxy implements InvocationHandler { private Object subject; public DynamicProxy(Object subject) { this.subject = subject; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("before calling " + method); method.invoke(subject, args); System.out.println("after calling " + method); return null; } } public class Main { public static void main(String[] args) { RealSubject realSubject = new RealSubject(); InvocationHandler handler = new DynamicProxy(realSubject); Subject subject = (Subject) Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject.getClass().getInterfaces(), handler); subject.request(); } } ``` 下面是一个CGLib动态代理的例子: ```python import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; class RealSubject { public void request() { System.out.println("RealSubject request"); } } class DynamicProxy implements MethodInterceptor { private Object subject; public DynamicProxy(Object subject) { this.subject = subject; } public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("before calling " + method); proxy.invoke(subject, args); System.out.println("after calling " + method); return null; } } public class Main { public static void main(String[] args) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(RealSubject.class); enhancer.setCallback(new DynamicProxy(new RealSubject())); RealSubject subject = (RealSubject) enhancer.create(); subject.request(); } } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值