JDK的动态代理原理剖析

基本代码实现

        首先jdk的动态代理代理的对象一定需要实现一个或多个接口。

代理接口

public interface Say {

    public void say();
}

代理接口实现类

public class User implements Say{
    @Override
    public void say() {
        System.out.println("hello");
    }
}

代理接口拦截器

public class UserHandle implements InvocationHandler {

    private Say user = null;

    public UserHandle(Say user){
        this.user = user;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //注释内容后面会说明
        /*Class<?> aClass = proxy.getClass();
        for (Class<?> anInterface : aClass.getInterfaces()) {
            System.out.println(anInterface.getName());
        }
        System.out.println(aClass.getSuperclass());*/
        method.invoke(user, args);
        return null;
    }
}

创建代理对象

public static void main(String[] args) {
        Say user = new User();
        UserHandle userHandle =new UserHandle(user);
        Say o = (Say) Proxy.newProxyInstance(user.getClass().getClassLoader(), user.getClass().getInterfaces(), userHandle);
        o.say();
        //System.out.println(user.getClass().getClassLoader().equals(new String("hello").getClass().getClassLoader()));
        //System.out.println(new String("hello").getClass().getClassLoader());
        //generatorProxy();

    }

代码原理剖析

1. 如何生成代理对象

通过调用  Proxy  类里面的静态方法  :

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

返回一个代理类。

1.1 newProxyInstance方法参数解读

        第一个参数:ClassLoader load  ,需要传入一个类加载器,可通过 

类名.class.getClassLoader()获得,实例化类.getClass().getClassLoader()获得。类加载器最好用代理的类获取,上面的例子我用的是代理类获取的(user) 。

补充一点:通过我们自己编写的类去获取的 类加载器 都是 app类加载器

这里我们可以看到获得的类加载器都是同一个加载器,所以第一个参数也可以填 通过其他自己创建的类 获取的类加载器,自己可尝试。原因是jvm底层有三个类加载器,app类加载器 是专门用于加载我们自己创建的类,其他两个类加载器是用来加载 jdk 自己底层的类。具体关于类加载器可自行查找资料。

        第二个参数:Class<?>[] interfaces ,需要代理的接口,通过:

 类实例.getClass().getInterfaces() 或  类.class().getInterfaces() 获得。

        第三个参数:InvocationHandler h  这里通过实现 InvocationHandler接口,并实现其 invoke()方法,在将 实例 传入其中。

2. invoke() 方法解读

public Object invoke(Object proxy, Method method, Object[] args)

        第一个参数:Object proxy :生成的代理对象,及上面我的例子  o 。

Say o = (Say) Proxy.newProxyInstance(user.getClass().getClassLoader(), user.getClass().getInterfaces(), userHandle);

为什呢?

我们可通过这段代码获取 jdk 创建的代理对象模板

public static void generatorProxy(){
        byte[] $Proxy0s = ProxyGenerator.generateProxyClass("$Proxy0", User.class.getInterfaces());
        try (FileOutputStream fileOutputStream = new FileOutputStream(new File("target\\classes\\com\\proxytest\\$Proxy0.class"))){
            fileOutputStream.write($Proxy0s);
            fileOutputStream.flush();
        } catch (Exception e) {
            return;
        }

    }

文件地址自己可修改,是生成的字节码文件所在地址。得到的字节码文件:

import com.proxytest.Say;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements Say {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    public $Proxy0(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 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 void say() throws  {
        try {
            super.h.invoke(this, m3, (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"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("com.proxytest.Say").getMethod("say");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

 我们可以看到创建的代理对象的继承关系:

public final class $Proxy0 extends Proxy implements Say {

 当调用say()方法的时候

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

会调用invoke()方法,传入的第一个参数为自己本身,super.h 获得对象为我们自己创建的 实现了 InvocationHandle的对象 ,因为其继承了Proxy类,创建代理对象时将其放入了进去。

通过下面代码可加以验证:

@Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Class<?> aClass = proxy.getClass();
        for (Class<?> anInterface : aClass.getInterfaces()) {
            System.out.println(anInterface.getName());
        }
        System.out.println(aClass.getSuperclass());


        method.invoke(user, args);
        return null;
    }

结果:

 可见其父类和接口与其上相吻合。

        第二个参数:Method method : 为代理的方法,代理对象调用的是哪个方法,则传入的是那个方法

例如:我写的例子调用的是 say()方法,当调用方法时传入的参数 m3:

 是通过静态代码块初始化而来:

 获得代理接口的say()方法。

        第三个参数:Object[] args :放传入的参数

最后,希望各位能给个免费的赞 ^-^ ,给作者一点鼓励。

  • 8
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值