Java基础-了解一下jdk的动态代理的本质

先简短的回顾下jdk动态代理用法

1.定义一个基础的接口

public interface Service {
    void print();
}
复制代码

2.简单的实现一下接口

public class MyService implements Service {
    @Override
    public void print() {
        System.out.println("this is print");
    }
}
复制代码

3.实现jdk的InvocationHandler接口

public class MyHandler implements InvocationHandler {

    private Service service;

    public MyHandler(Service service){
        this.service = service;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("this is before!");
        Object result = method.invoke(service, args);
        System.out.println("this is after!");
        return result;
    }
}
复制代码

4.调用Proxy类实现动态增强

public static void main(String[] args) {
        Service service = new MyService();
        Service proxyInstance = (Service) Proxy.newProxyInstance(Demo.class.getClassLoader(), new Class[]{Service.class}, new MyHandler(service));
        proxyInstance.print();
    }
复制代码

如果不出意外的话,控制台会打印出如下信息

this is before!

this is print

this is after!

这说明我们写的方法,得到了增强!

JDK动态代理的原理

1.jdk动态代理的实质是什么?

不知道大家有没有想过,这行代码

Proxy.newProxyInstance(Demo.class.getClassLoader(), new Class[]{Service.class}, new MyHandler(service));
复制代码

返回的究竟是个什么东西?

带着疑问,我们先使用反射打印一下类名试试。

System.out.println(proxyInstance.getClass().getName());
复制代码

得到的结果为

com.sun.proxy.$Proxy0
复制代码

很显然这个类并不是我们创建的。所以到这儿就应该想到了,动态代理实质,就是使用字节码技术,重新生成了一个新类,来达到增强的效果。

那么增强的新类到底是个怎样的类呢?我们来挖掘一下动态代理的源码。

2.jdk动态代理源码分析

分析源码的时候我一般都是根据方法的参数和返回值,大致推敲一下方法的功能,这样可以快速找到关键方法。所以后面的代码都是被我精简过的,大家可以对比着源码阅读。

首先进入newProxyInstance方法,在去除掉业务不相干代码后如下:

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
                                          
            Class<?> cl = getProxyClass0(loader, intfs);
            
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            
            return cons.newInstance(new Object[]{h});
    }
复制代码
  1. 第一步通过getProxyClass0方法,获得了一个class对象cl
  2. 第二步获得了cl对象的构造函数,这个构造函数的参数是一个 InvocationHandler类型
  3. 第三步通过我们传入的InvocationHandler接口实现类h构造了cl对象的实例。

也就是新类就算这个cl,弄清除cl是啥,动态代理的原理我们就基本弄懂了。所以跟着这个目标,进入getProxyClass0方法。

 private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        return proxyClassCache.get(loader, interfaces);
}
复制代码

除去校验的方法外,只剩下一行代码。所以只能在进去看看。在进入之前,先看看proxyClassCache是啥。

    private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
        
    public WeakCache(BiFunction<K, P, ?> subKeyFactory,
                     BiFunction<K, P, V> valueFactory) {
        this.subKeyFactory = Objects.requireNonNull(subKeyFactory);
        this.valueFactory = Objects.requireNonNull(valueFactory);
    }
复制代码

现在在进入proxyClassCache的get方法里,进入get方法咋一看代码有点多,其实上面都是缓存相关处理(缓存的话我建议等看完主流程再回头看,那样更有助于理解缓存),我们先跳过,直接看关键代码

while (true) {
            if (supplier != null) {
                V value = supplier.get();
                if (value != null) {
                    return value;
                }
            }
            if (factory == null) {
                factory = new Factory(key, parameter, subKey, valuesMap);
            }

            if (supplier == null) {
                supplier = valuesMap.putIfAbsent(subKey, factory);
                if (supplier == null) {
                    supplier = factory;
                }
            } 
        }
复制代码

关键代码经简化后如上,这样就能清晰的看出supplier就是new Factory(key, parameter, subKey, valuesMap)的实例。所以进入supplier的get方法。

public synchronized V get() { // serialize access
            try {
                value = Objects.requireNonNull(valueFactory.apply(key, parameter));
            } finally {
                if (value == null) { // remove us on failure
                    valuesMap.remove(subKey, this);
                }
            }
            return value;
        }
    }
复制代码

进入get方法后,关键代码就一句valueFactory.apply(key, parameter),而这个valueFactory就是上面在创建WeakCache时设置的ProxyClassFactory。所以进入ProxyClassFactory的apply方法。

在apply方法中,终于看到了创建代理类的关键方法

       byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
       proxyName, interfaces, accessFlags);
       return defineClass0(loader, proxyName,
                            proxyClassFile, 0, proxyClassFile.length);
复制代码

其中proxyClassFile就是新类的字节码,而defineClass0方法,就是加载这个新类。对于字节码如何构造在这儿我就不深究了。感兴趣的可以自己研究。我更关心新类的结构。所以既然找到了新类的生成方法,我们就将他打印到文件中瞧一瞧。

        byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                "ProxyService", new Class[]{Service.class}, Modifier.FINAL);
        OutputStream outputStream = new FileOutputStream(new File("d:/JdkProxy.class"));
        outputStream.write(proxyClassFile);
复制代码

在d盘下找到这个文件,直接用idea打开。

final class ProxyService extends Proxy implements Service {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

    public ProxyService(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("study.demo.jdk_proxy.Service").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());
        }
    }
}
复制代码

不出意外就会看到如上代码了。其实看到这个代码后,jdk动态代理的本质心里就应该有底了。所以后面就不用再说下去了吧。

下一篇,分析一下cglib动态代理的本质

转载于:https://juejin.im/post/5c850c6df265da2da23d618d

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值