对于动态代理的执行过程及源码解析简单说下,很通俗易懂

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Fire_Sky_Ho/article/details/89283880

底层代码根据我自己不完全了解,应该是实现被代理类的接口,然后代理类调用代理接口的里方法时,会去调用InvocationHandler 里重写的的invoke实现的方法,invoke方法里包括了原本的要执行的方法和增强的逻辑代码。

定义一个父类接口

public interface Fu {
    public String say();
}

定义一个子类实现父类

public class Zi implements Fu {
    @Override
    public String say() {
        System.out.println("我是Zi类");
        return "我是Zi类的say方法的返回值";
    }
}

实现并测试动态代理类

public class ProxyText {
    public static void main(String[] args) {
        Zi z = new Zi();
        Fu f = (Fu) Proxy.newProxyInstance(z.getClass().getClassLoader(), z.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("代理前。。。。");
                if (method.getName() == "say") {
                    Object o = method.invoke(z);
                    System.out.println(o);
                }
                System.out.println("代理后。。。。");
                return "代理类";
            }
        });
        String s = f.say();
        System.out.println(s);
    }
}

输出:

代理前。。。。
我是Zi类
我是Zi类的say方法的返回值
代理后。。。。
代理类

使用注意:

1.一般情况下JDK的动态代理类只能代理有接口的类,想要代理没接口的必须使用cglib。

2.Fu f = (Fu) Proxy.newProxyInstance,要强制转换成被代理类的接口

先了解下一些要懂的知识:

1.通过实现 InvocationHandler 接口创建自己的调用处理器,当使用者调用了代理对象所代理的接口中的方法的时候,就会去执行InvocationHandler 里的内容

2.Proxy 里有个构造方法,传进来的是InvocationHandler 这个类

newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

第三个参数

 protected Proxy(InvocationHandler h) {
        Objects.requireNonNull(h);
        this.h = h;
    }

$Proxy0代理类会继承这个类,所以Proxy(InvocationHandler h)也会被继承,h里面就有invoke方法,invoke放法里面有我们要增强的逻辑。

3.看看NewProxyInstance方法生成的$Proxy0代理类的源码,大致如下,照着别人改的,自己不会生成o(* ̄︶ ̄*)o

public final class $Proxy0 extends Proxy implements Fu {
    private static Method m1;
    private static Method m0;
    private static Method m3;
    private static Method m2;
 
    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals",
                    new Class[] { Class.forName("java.lang.Object") });
 
            m0 = Class.forName("java.lang.Object").getMethod("hashCode",
                    new Class[0]);
 
            m3 = Class.forName("***.Zi").getMethod("say",
                    new Class[0]);
 
            m2 = Class.forName("java.lang.Object").getMethod("toString",
                    new Class[0]);
 
        } catch (NoSuchMethodException nosuchmethodexception) {
            throw new NoSuchMethodError(nosuchmethodexception.getMessage());
        } catch (ClassNotFoundException classnotfoundexception) {
            throw new NoClassDefFoundError(classnotfoundexception.getMessage());
        }
    } 
 
    /*super(paramInvocationHandler),是调用父类Proxy的构造方法,就是前面那个我说的构造方法
    Proxy(InvocationHandler h)*/
    public $Proxy0(InvocationHandler invocationhandler) {
        super(invocationhandler);
    }
 
    @Override
    public final boolean equals(Object obj) {
        try {
            return ((Boolean) super.h.invoke(this, m1, new Object[] { obj })) .booleanValue();
        } catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }
 
    @Override
    public final int hashCode() {
        try {
            return ((Integer) super.h.invoke(this, m0, null)).intValue();
        } catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }
 
    public final void say() {
        try {
            super.h.invoke(this, m3, null); //就是这个地方  调用h.invoke()
            return;
        } catch (Error e) {
        } catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }
 
    @Override
    public final String toString() {
        try {
            return (String) super.h.invoke(this, m2, null);
        } catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }
}
 

来看看这个源码,,里面主要有两个方法值得引起我们注意:

Ⅰ.

    public $Proxy0(InvocationHandler invocationhandler) {
        super(invocationhandler);
    }

super(paramInvocationHandler),是调用父类Proxy的构造方法,就是前面那个我说的构造方法
Proxy(InvocationHandler h),这段就是执行了构造方法

ⅠⅠ.

  public final void say() {
        try {
            super.h.invoke(this, m3, null); //就是这个地方  调用h.invoke()
            return;
        } catch (Error e) {
        } catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }

①:当调用f.say()方法时,其实调用的$Proxy0的say()方法,从而调用父类Proxy中传进来第三个参数(h)的的Invoke方法。

②:h.invoke的h就是我们继承父类那个传进来的h,而这个h的invoke方法就是调用newProxyInstance里第三个参数里面的invoke方法,m3就是这个方法。

③:

this.h:看Proxy的源码,发现h就是Proxy类中的InvocationHandler h,并且在指向了Zi实例

this:指此动态代理类$Proxy0本身

m3:看静态代码块,指的就是反射生成的Fu接口的say()方法的Method对象,我也不是很懂,暂时不理他

④:h.invoke(this, m3, null)方法并不是反射的invoke()方法,只是普通的调用h重写的invoke方法,调用时就将动态代理类$Proxy0本身、m3为Fu反射的Method

⑤然后在反射调用的代码前后,可以插入自定义的一些代码,就是增强方法了

该了解的讲完,大致的执行过程过一遍

调用f.say()

首先生成一个缓存在java虚拟机中的$Proxy0代理类,里面有两个主要的东西

InvocationHandler h:h里有个invoke方法,就是我们重写的方法

say()方法:执行了h里的invoke方法,并把这个$Proxy0类,say()方法和方法参数传递过去

开始执行$Proxy0.say()方法里的h.invoke()

进入到public Object invoke(Object proxy, Method method, Object[] args)里

System.out.println("代理前。。。。");

method.invoke(z);

这个z就是被代理类,method就是接口的方法,所以整个就是调用Zi类里的say()方法

System.out.println("代理后。。。。");

return "代理类";

f.say()接收这个"代理类",并打印输出

总结

这篇文章,不一定准确,我仅仅只是对源码和执行过程大致了解了下而已,写出的这个过程和解析,应该是会有点错误,不过整体还是对的,等我那天牛逼了都懂了,我就回来修改补充

猜你喜欢

转载自blog.csdn.net/Fire_Sky_Ho/article/details/89283880