欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

JDK动态代理的底层实现原理

程序员文章站 2024-03-14 20:24:47
...

JavaEE的开发中,许多框架用到了动态代理机制,例如Spring的AOP编程。
这里不介绍动态代理和静态代理概念,有兴趣的朋友自行百度。

Java中的动态代理一般就两种:1. JDK自带 ; 2. cglib库
由于笔者没用到cglib,所以这里介绍的是JDK动态代理的使用和原理分析。

注意: 本文使用的JDK版本是JDK 1.8, 编译器 IntelliJ IDEA 2019.2.1。阅读本文需要有Java反射知识,Hash数据结构知识,IO操作

使用,代码:

/**
 * JDK动态代理必须的接口
 */
public interface IProducer {
    /**
     * 售前
     *
     * @param money
     */
    float preSale(float money);
    /**
     * 售后
     *
     * @param money
     */
    float afterSale(float money);
}
/**
 * 接口实现类
 */
public class Producer implements IProducer {
    @Override
    public float preSale(float money) {
        System.out.println("售前价格:" + money);
        return money;
    }

    @Override
    public float afterSale(float money) {
        System.out.println("售后价格: " + money);
        return money;
    }
}
/**
 * 测试类
 */
public class MyClient {

    public static void main(String[] args) {
        final IProducer producer = new Producer();

        /**
         * JDK动态代理被代理类最少实现一个接口,如果没有则不能使用
         * 可以看到{@link Proxy#newProxyInstance(ClassLoader, Class[], InvocationHandler)}
         * 这个方法并没有使用使用producer这个对象,所以,创建出来的对象是另一个对象
         */
        final IProducer iProducer = (IProducer) Proxy.newProxyInstance(producer.getClass().getClassLoader(),
                producer.getClass().getInterfaces()
                , (proxy, method, args1) -> {
                    float money = (float) args1[0];
                    //增强一下售前方法,打折
                    Object result = null;
                    System.out.println(producer == proxy);
                    System.out.println(producer.equals(proxy));
                    System.out.println(producer.getClass().getName());
                    System.out.println(proxy.getClass().getName());
                    if ("preSale".equals(method.getName())) {
                        money *= 0.8f;//打八折
                        System.out.println("售前打八折优惠");
                        result = method.invoke(producer, money);
                    } else {
                        result = method.invoke(producer, args1);
                    }

                    return result;
                });
        float v = iProducer.preSale(88f);
        System.out.println("售前最终价格: " + v);
        float v1 = iProducer.afterSale(100f);
        System.out.println("售后最终价格: " + v1);
    }
}

打印结果:

false
false
com.itheima.practice.Producer
com.sun.proxy.$Proxy0
售前打八折优惠
售前价格:70.4
售前最终价格: 70.4
false
false
com.itheima.practice.Producer
com.sun.proxy.$Proxy0
售后价格: 100.0
售后最终价格: 100.0

使用细节分析:
从上面的使用,可以看到使用动态代理好处:

  1. 字节码随用随创建,随用随加载
  2. 在不修改源码的基础上对方法增强

JDK的动态代理是基于接口的,使用Proxy#newProxyInstance(ClassLoader, Class[], InvocationHandler)创建一个代理对象,需要传入被代理对象的类加载器、被代理对象的接口数组,InvocationHandler的实例。
上面打印结果可以看到,代理对象和真正的对象不是同一个对象。

通过InvocationHandler实例里面的方法,最终在不修改原来代码的基础上增强我们的代码功能。
使用就是这么简单,下面来分析一下JDK动态代理原理

原理分析:

可以看到,我们通过Proxy#newProxyInstance(ClassLoader, Class[], InvocationHandler)创建代理对象,那么重点就是分析这个方法。 贴出方法实现(去掉异常,安全检查,公有私有访问权限等判断):

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

        final Class<?>[] intfs = interfaces.clone();//接口数组拷贝,没啥好说的

        /*
         * Look up or generate the designated proxy class.
         */
        Class<?> cl = getProxyClass0(loader, intfs);//看好关键点不迷路【1】

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        final Constructor<?> cons = cl.getConstructor(constructorParams);//获取构造方法
		//反射创建代理对象(后面的代码跟踪可以知道,这个cons是一个class类字节码数组,我们知道class要加载到JVM中才能运行的,所以这里直接看成是class)
        return cons.newInstance(new Object[]{h});
    }

其中三条语句比较简单,就不细说了

final Class<?>[] intfs = interfaces.clone();//接口数组拷贝,没啥好说的
final Constructor<?> cons = cl.getConstructor(constructorParams);//获取构造方法
return cons.newInstance(new Object[]{h});//反射创建代理对象

重点在于 Class<?> cl = getProxyClass0(loader, intfs); 方法。跟踪进去:

private static Class<?> getProxyClass0(ClassLoader loader,
                                       Class<?>... interfaces) {
    if (interfaces.length > 65535) {
        throw new IllegalArgumentException("interface limit exceeded");
    }
	// If the proxy class defined by the given loader implementing
	// the given interfaces exists, this will simply return the cached copy;
	// otherwise, it will create the proxy class via the ProxyClassFactory
	//看好关键点不迷路【2】
    return proxyClassCache.get(loader, interfaces);
}

可以看到调用并返回了 return proxyClassCache.get(loader, interfaces); 这里要特别注意这个static 成员属性: proxyClassCache,它是一个私有静态成员属性,在类加载的时候就进行了初始化初始化, 是一个 WeakCache 对象。

/**
 * 看好关键点不迷路【3】
 */
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
    proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

//KeyFactory对象和ProxyClassFactory对象作为WeakCache成员属性保存了起来
public WeakCache(BiFunction<K, P, ?> subKeyFactory,
                 BiFunction<K, P, V> valueFactory) {
    this.subKeyFactory = Objects.requireNonNull(subKeyFactory);
    this.valueFactory = Objects.requireNonNull(valueFactory);
}

同时我们要注意到 return proxyClassCache.get(loader, interfaces); 语句上面的注释,大概意思是: 
如果定义的代理类装入器实现给定的接口存在,这将仅返回缓存副本;否则,它将通过ProxyClassFactory创建代理类。 OK,该处注释明确提示了是由ProxyClassFactory创建代理类。后面的分析会证实这一点。
而KeyFactory对象和ProxyClassFactory对象是在初始化proxyClassCache的时候传进去的。 这两个对象都要记住。等下会有点绕

继续跟踪proxyClassCache.get(loader, interfaces) 方法, 请看注释:

public V get(K key, P parameter) {
    Objects.requireNonNull(parameter);//不重要

    expungeStaleEntries();//不重要

	//hash计算
    Object cacheKey = CacheKey.valueOf(key, refQueue);
    
    //缓存操作,没啥特别的
    ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
    if (valuesMap == null) {
        ConcurrentMap<Object, Supplier<V>> oldValuesMap
            = map.putIfAbsent(cacheKey,
                              valuesMap = new ConcurrentHashMap<>());
        if (oldValuesMap != null) {
            valuesMap = oldValuesMap;
        }
    }

	//subKeyFactory是一个KeyFactory对象。这里获取一个key,接口parameter的hash key
    Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
    //开始进入重点了,通过上面的subKey获取一个Supplier对象
    Supplier<V> supplier = valuesMap.get(subKey);//第一次,里面肯定拿不到对象的, 为null
    Factory factory = null;//注意这里为null

	//跟踪代码重点来了,不要看晕哦
    while (true) {
        if (supplier != null) {//循环第一次肯定不命中
        	//看好关键点不迷路【4】
            V value = supplier.get();
            if (value != null) {
                return value;//这里返回class字节码数组
            }
        }
		//循环第一次肯定命中,注意了,创建了一个Factory对象,还要注意Factory是WeakCache的内部类。
        if (factory == null) {
        	//看好关键点不迷路【5】
            factory = new Factory(key, parameter, subKey, valuesMap);
        }
		
		//第一次循环必定命中
        if (supplier == null) {
			//缓存起来
			//ConcurrentHashMap#putIfAbsent()方法如果不存在(新的entry),那么会向map中添加该键值对,并返回null。 如果已经存在,那么不会覆盖已有的值,直接返回已经存在的值
            supplier = valuesMap.putIfAbsent(subKey, factory);//因为是第一次缓存所以这里supplier依然为null
            if (supplier == null) {//第一次循环命中
            	//看好关键点不迷路【6】
                supplier = factory;//第一次循环中赋值
            }
        } else {
            if (valuesMap.replace(subKey, supplier, factory)) {
                supplier = factory;
            } else {
                supplier = valuesMap.get(subKey);
            }
        }
    }
}

为避免混乱,上面代码注释中只写了第一次循环的情况,这里继续说下一次循环。
现在可以知道的是,第一循环supplier不再为null,supplier = factory。
重点来了,注意这个Factory(它是WeaCache的内部类)
当开始第二次循环:

 while (true) {
	 //循环第二次命中
	if (supplier != null) {
		//看好关键点不迷路【4】supplier就是初始化的 Factory 对象,所以这里调用Factory#get()方法
	    V value = supplier.get();
	    if (value != null) {
	        return value;
	    }
	}
	//.......
}

可以看到最终调用了Factory#get()方法,上面提醒过多次Factory是WeaCache的内部类。

private final class Factory implements Supplier<V> {

    private final K key;
    private final P parameter;
    private final Object subKey;
    private final ConcurrentMap<Object, Supplier<V>> valuesMap;

    Factory(K key, P parameter, Object subKey,
            ConcurrentMap<Object, Supplier<V>> valuesMap) {
        this.key = key;
        this.parameter = parameter;
        this.subKey = subKey;
        this.valuesMap = valuesMap;
    }

    @Override
    public synchronized V get() { // serialize access
        // re-check
        Supplier<V> supplier = valuesMap.get(subKey);
        if (supplier != this) {
            return null;
        }
        V value = null;
        try {
	        //看好关键点不迷路【7】 还记得Proxy#newProxyInstance(ClassLoader, Class[], InvocationHandler) -> Proxy#getProxyClass0() 吗?成员属性proxyClassCache初始化时的第二个参数ProxyClassFactory对象,因为Factory是WeakCache的内部类,可以访问此valueFactory,所以这里调用的就是ProxyClassFactory#apply()方法
            value = Objects.requireNonNull(valueFactory.apply(key, parameter));
        } finally {
            if (value == null) { 
                valuesMap.remove(subKey, this);
            }
        }
        assert value != null;
        
        CacheValue<V> cacheValue = new CacheValue<>(value);

        if (valuesMap.replace(subKey, this, cacheValue)) {
            // put also in reverseMap
            reverseMap.put(cacheValue, Boolean.TRUE);
        } else {
            throw new AssertionError("Should not reach here");
        }
        //这里最终作为一个代理类返回出去了
        return value;
    }
}

看注释, 调用了ProxyClassFactory#apply()方法

private static final class ProxyClassFactory
    implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
	//这里就是为什么JDK动态代理类类名都有 $Proxy 字段的原因
    private static final String proxyClassNamePrefix = "$Proxy";
    //原子类
    private static final AtomicLong nextUniqueNumber = new AtomicLong();

    @Override
    public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

        Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
        for (Class<?> intf : interfaces) {
            Class<?> interfaceClass = null;
            try {
            	//加载接口
                interfaceClass = Class.forName(intf.getName(), false, loader);
            } catch (ClassNotFoundException e) {
            }
            if (interfaceClass != intf) {
                throw new IllegalArgumentException(
                    intf + " is not visible from class loader");
            }

            if (!interfaceClass.isInterface()) {
                throw new IllegalArgumentException(
                    interfaceClass.getName() + " is not an interface");
            }

            if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                throw new IllegalArgumentException(
                    "repeated interface: " + interfaceClass.getName());
            }
        }

        String proxyPkg = null;     // package to define proxy class in
        int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

		//对接口名字符串进行操作
        for (Class<?> intf : interfaces) {
            int flags = intf.getModifiers();
            if (!Modifier.isPublic(flags)) {//正常流程,不会命中,所以proxyPkg一般为null
                accessFlags = Modifier.FINAL;
                String name = intf.getName();
                int n = name.lastIndexOf('.');
                String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                if (proxyPkg == null) {
                    proxyPkg = pkg;
                } else if (!pkg.equals(proxyPkg)) {
                    throw new IllegalArgumentException(
                        "non-public interfaces from different packages");
                }
            }
        }

		//一般情况下都会命中
        if (proxyPkg == null) {
            // if no non-public proxy interfaces, use com.sun.proxy package
            //这里就是为什么JDK动态代理类类名一般以 com.sun.proxy 作为前缀
            proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
        }

        long num = nextUniqueNumber.getAndIncrement();
        //最终的动态代理类名
        String proxyName = proxyPkg + proxyClassNamePrefix + num;

        /*
         * 看好关键点不迷路【8】找到了,就是这里,真正生成class字节文件的地方,
         */
        byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
            proxyName, interfaces, accessFlags);
        try {
        	//这是一个native方法,看不到。但我们可以猜一下...
            return defineClass0(loader, proxyName,
                                proxyClassFile, 0, proxyClassFile.length);
        } catch (ClassFormatError e) {
            throw new IllegalArgumentException(e.toString());
        }
    }
}

我们看到最终通过

byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
            proxyName, interfaces, accessFlags);

生成class字节码数组,里面是IO操作,方法代码就不贴了。

private static native Class<?> defineClass0(ClassLoader loader, String name,
                                            byte[] b, int off, int len);

是一个native方法,看不到具体实现。但我们可以猜一下:通过类加载器加载这个字节码数组到JVM中,所以实际上,最终就是返回这个class字节码数组。我们可以验证

byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
            proxyName, interfaces, accessFlags);

这个方法究竟是不是我们猜想的一样:生成了一个class字节码数组。既然是一个byte数组,我们直接用文件写出来不就可以了?
请看代码:

/**
 * JDK动态代理必须的接口
 */
public interface IProducer {

    /**
     * 售前
     *
     * @param money
     */
    float preSale(float money);

    /**
     * 售后
     *
     * @param money
     */
    float afterSale(float money);
}
import sun.misc.ProxyGenerator;

import java.io.*;
import java.lang.reflect.Modifier;

public class Test {

    public static void main(String[] args) {
        String className = "com.vincent.LinTest";//类名
        String fileName = className + ".class";//类文件名
        byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                "com.vincent.LinTest", new Class[]{IProducer.class}, Modifier.PUBLIC);

        try {
            FileOutputStream fos = new FileOutputStream(new File("C:\\Users\\tgvincent\\Desktop\\temp", fileName));
            fos.write(proxyClassFile);
            fos.flush();
            fos.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

执行,class文件生成成功
JDK动态代理的底层实现原理
看到生成了之后,那究竟正不正确?还是用IntelliJ IDEA打开

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.vincent;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public class LinTest extends Proxy implements IProducer {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;
    private static Method m4;

    public LinTest(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 float preSale(float var1) throws  {
        try {
            return (Float)super.h.invoke(this, m3, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

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

    public final float afterSale(float var1) throws  {
        try {
            return (Float)super.h.invoke(this, m4, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    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("IProducer").getMethod("preSale", Float.TYPE);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
            m4 = Class.forName("IProducer").getMethod("afterSale", Float.TYPE);
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

可以看到生成的class文件,包名为 com.vincent,类名为LinTest,继承Proxy类,实现了IProducer的接口方法。

所以可以得出,JDK动态代理原理就是,在内存中生成class字节码数组,由JVM加载,然后反射调用构造方法生成代理对象。
梳理一下方法调用流程:

Proxy#newProxyInstance(ClassLoader, Class[], InvocationHandler)

Proxy#getProxyClass0(ClassLoader loader, Class<?>... interfaces)

WeakCache#get(K key, P parameter)

Factory#get()

ProxyClassFactory#apply(ClassLoader loader, Class<?>[] interfaces)

ProxyGenerator#generateProxyClass(final String proxyName, Class<?>[] interfaces, int accessFlags)

那么现在还有一个问题:调用代理对象方法为什么可以调用 InvocationHandler#invoke(Object proxy, Method method, Object[] args) throws Throwable 方法, 进而在此方法中反射调用增强原来的方法?

要回答这个问题,可以通过生成的 com.vincent.LinTest.class 字节码文件结合上面梳理的方法调用进行分析:

首先我们已经知道 Proxy#newProxyInstance(ClassLoader, Class[], InvocationHandler)会生成一个代理对象,而此方法中有一行代码调用:

final Constructor<?> cons = cl.getConstructor(constructorParams);

这一句获取一个构造方法对象,其方法参数类型是 InvocationHandler.class,而在 com.vincent.LinTest.class 字节码文件中体现为下面这个构造方法:
JDK动态代理的底层实现原理
接着另一句代码:

return cons.newInstance(new Object[]{h});

这里就是通过构造方法对象反射调用获取一个代理对象,
new Object[]{h} 中的h会把 InvocationHandler 对象作为参数传递了进去,
由父类Proxy中 protected InvocationHandler h; 成员属性把此对象保存起来。

接下来,注意 com.vincent.LinTest.class 字节码文件中的 静态代码块:

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("IProducer").getMethod("preSale", Float.TYPE);
        m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        m4 = Class.forName("IProducer").getMethod("afterSale", Float.TYPE);
    } catch (NoSuchMethodException var2) {
        throw new NoSuchMethodError(var2.getMessage());
    } catch (ClassNotFoundException var3) {
        throw new NoClassDefFoundError(var3.getMessage());
    }
}

我们以调用方法 IProducer#preSale(88f);为例,在该static代码块中

m3 = Class.forName("IProducer").getMethod("preSale", Float.TYPE);

获取到了preSale的方法对象,当我们调用代理对象的 preSale(float)方法时会调用方法

public final float preSale(float var1) throws  {
    try {
        return (Float)super.h.invoke(this, m3, new Object[]{var1});//回调InvocationHandler对象方法
    } catch (RuntimeException | Error var3) {
        throw var3;
    } catch (Throwable var4) {
        throw new UndeclaredThrowableException(var4);
    }
}

而这个 h 就是一个 InvocationHandler 对象,这里就是为什么执行了

InvocationHandler#invoke(Object proxy, Method method, Object[] args) throws Throwable

这个方法,并且这里也解释了为什么动态代理想要执行原对象方法需要传递原对象进去,因为这个this是指代理对象,而不是原对象。

至此JDK动态代理原理分析完毕。