jdk动态代理源码以及缓存结构分析

概述

jdk动态代理是基于接口的代理,其在运行期间创建对应接口的代理类字节码class并将字节码class装载进虚拟机实现代理。

简单jdk动态代理例子

我们通过对代理对象创建代理类后,可以在代理对象的被代理方法执行前后增加额外的逻辑,可以修改被代理方法的参数和执行返回结果,甚至可以控制被代理方法是否执行。

public class JdkProxyTest {
    public static void main(String[] args) {
        Itf proxy = (Itf) Proxy.newProxyInstance(MyInvocationHandler.class.getClassLoader(), new Class[]{Itf.class}, new MyInvocationHandler(new ItfImpl()));
        Object res = proxy.fun1("测试参数");
        System.out.println(res);
    }
}

interface Itf {
    Object fun1(Object param);
}

class ItfImpl implements Itf {

    @Override
    public Object fun1(Object param) {
        System.out.println("ItfImpl->fun1() executing···,输入参数是:"+param);
        return "测试结果";
    }
}

class MyInvocationHandler implements InvocationHandler {
    private Itf itf;

    public MyInvocationHandler(Itf itf) {
        this.itf = itf;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("在方法:" + method.getName() + " 执行前织入其它逻辑···");
        Object invoke = method.invoke(itf, args[0]+ " 修改原方法参数");
        System.out.println("do sth after method:" + method.getName() + " executing···");
        System.out.println("在方法:" + method.getName() + " 执行后织入其它逻辑···");
        return invoke + " 修改原方法执行结果";
    }
}

测试结果如下:

在方法:fun1 执行前织入其它逻辑···
ItfImpl->fun1() executing···,输入参数是:测试参数 修改原方法参数
do sth after method:fun1 executing···
在方法:fun1 执行后织入其它逻辑···
测试结果 修改原方法执行结果

添加VM参数:-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true可以将生成的动态代理类输出到文件,这个文件默认处于当前项目的jdk.proxy包下:本测试项目生成代理类如下:

package jdk.proxy;

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

final class $Proxy0 extends Proxy implements Itf {
    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 Object fun1(Object var1) throws  {
        try {
            return (Object)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);
        }
    }

    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("jdk.proxy.Itf").getMethod("fun1", Class.forName("java.lang.Object"));
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

可以看到生成的代理类默认继承了Proxy类并实现了相关代理接口,这也是为什么jdk动态代理只能代理接口的原因(Java单继承),至于为什么要继承Proxy类,从生成的代理中可以看到除了使用super().h.invoke···之外没有使用到Proxy类中的其它方法,因此我断定原因就是乌龟的屁股:龟腚。
此外所有的代理的接口的方法都通过静态成员变量标识,避免反射获取方法降低效率,hashCode,equals,toString均被固定代理。
构造方法中传入一个InvocationHandler最终被赋值给Proxy的h变量,这也是继承Proxy的唯一使用地方

源码分析

jdk创建代理类通过Proxy#newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)方法实现,该方法要求传入3个参数

  • loader:构造代理类的类加载器
  • interfaces:被代理类实现的一系列接口
  • h:InvocationHandler的实例,实现具体的代理逻辑
@CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);
		//复制接口信息
        final Class<?>[] intfs = interfaces.clone();
        //权限校验
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        //创建代理类的Class对象,即创建字节码信息并加载到虚拟机中
        Class<?> cl = getProxyClass0(loader, intfs);
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }
			//获取构造器方法
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            //如果构造器方法的访问权限不是public,反射设置其为可访问状态
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            //反射调用构造器方法创建代理实例
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }

从上述源码可以发现代理类的创建逻辑处于getProxyClass0方法中,该方法如下:

    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
        //对代理类使用缓存功能,如果类加载器对应的接口代理类已存在,则返回缓存副本,否则通过ProxyClassFactory创建代理类缓存后并返回
        //其中proxyClassCache被默认初始化为:new WeakCache<>(new KeyFactory(), new ProxyClassFactory())
        return proxyClassCache.get(loader, interfaces);
    }

proxyClassCache.get(loader, interfaces)方法逻辑如下

    public V get(K key, P parameter) {
        Objects.requireNonNull(parameter);
		//清理缓存,使用Map缓存,其key使用WeakReference,因此key可能会被垃圾回收器回收,当key被回收后(放入referenceQueque中)通过该key回收其value
        expungeStaleEntries();
		//构建可被自动回收的cacheKey(主要方式代理类过多占用内存),这里传入的key是ClassLoader,因此cacheKey作为一级缓存以类加载器作为区分
        Object cacheKey = CacheKey.valueOf(key, refQueue);

        // lazily install the 2nd level valuesMap for the particular cacheKey
        //先判断缓存map是否存在,如果不存在则初始化缓存value map
        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;
            }
        }

        // create subKey and retrieve the possible Supplier<V> stored by that
        // subKey from valuesMap
        //使用keyFactory构建subkey,这里有点绕,简单来说cacheKey作为一级映射获取key获取该类加载器下的缓存map,再通过subKey(二级缓存key,类加载器和对应代理接口)获取最终的缓存信息,最终的缓存信息是一个函数式接口,通过该接口获取代理类实例Class
        Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
        Supplier<V> supplier = valuesMap.get(subKey);
        Factory factory = null;
        while (true) {
            if (supplier != null) {
                // supplier might be a Factory or a CacheValue<V> instance
                //通过函数接口获取Class对象,这里supplier有两种实现,首次创建代理类时为Factory类的实例,创建好后使用CacheKey封装代理Class对象替换Factory实例,其中Factory和Cache都是Supplier的函数接口
                V value = supplier.get();
                if (value != null) {
                    return value;
                }
            }
            // else no supplier in cache
            // or a supplier that returned null (could be a cleared CacheValue
            // or a Factory that wasn't successful in installing the CacheValue)

            // lazily construct a Factory
            //初次调用或由于cacheKey被回收后都会触发factory的新构建
            if (factory == null) {
                factory = new Factory(key, parameter, subKey, valuesMap);
            }
			
            if (supplier == null) {
            	//添加到二级缓存,此时需要重新生成字节码
                supplier = valuesMap.putIfAbsent(subKey, factory);
                if (supplier == null) {
                    // successfully installed Factory
                    supplier = factory;
                }
                // else retry with winning supplier
            } else {
            	//在此期间cacheKey过期但缓存没有被回收则进行替换操作
                if (valuesMap.replace(subKey, supplier, factory)) {
                    // successfully replaced
                    // cleared CacheEntry / unsuccessful Factory
                    // with our Factory
                    supplier = factory;
                } else {
                    // retry with current supplier
                    supplier = valuesMap.get(subKey);
                }
            }
        }
    }

private void expungeStaleEntries()主要用于清理陈旧的缓存信息

    private void expungeStaleEntries() {
        CacheKey<K> cacheKey;
        //通过循环遍历refQueue来清理map和reverseMap,refQueue是由虚拟机进行维护的,详情参见java Reference
        while ((cacheKey = (CacheKey<K>)refQueue.poll()) != null) {
            cacheKey.expungeFrom(map, reverseMap);
        }
    }
        void cacheKey.expungeFrom(ConcurrentMap<?, ? extends ConcurrentMap<?, ?>> map,
                         ConcurrentMap<?, Boolean> reverseMap) {
            // removing just by key is always safe here because after a CacheKey
            // is cleared and enqueue-ed it is only equal to itself
            // (see equals method)...
            //当cacheKey被虚拟机自动回收后,则再调用该方法时将清理掉对应的value
            ConcurrentMap<?, ?> valuesMap = map.remove(this);
            // remove also from reverseMap if needed
            if (valuesMap != null) {
                for (Object cacheValue : valuesMap.values()) {
                    reverseMap.remove(cacheValue);
                }
            }
        }

上述代码中创建subKey通过:subKeyFactory.apply(key, parameter),subKeyFactory就是KeyFactory,其apply方法如下:

		@Override
        public Object apply(ClassLoader classLoader, Class<?>[] interfaces) {
        	//简单判断接口数量根据接口数量构建Key,注意Key1,Key2,Keyx用于保存接口的类都是弱引用类
            switch (interfaces.length) {
                case 1: return new Key1(interfaces[0]); // the most frequent
                case 2: return new Key2(interfaces[0], interfaces[1]);
                case 0: return key0;
                default: return new KeyX(interfaces);
            }
        }

最终通过Factory类(函数式接口实现)来获取Class类信息,源码如下:

@Override
public synchronized V get() { // serialize access
    // re-check
    //重复检测判断缓存map中缓存的Supplier和当前Supplier是否是同一对象
    Supplier<V> supplier = valuesMap.get(subKey);
    if (supplier != this) {
        // something changed while we were waiting:
        // might be that we were replaced by a CacheValue
        // or were removed because of failure ->
        // return null to signal WeakCache.get() to retry
        // the loop
        //多线程或者由于JVM不定时回收可能会导致该种情况,此时将返回null进入上层循环重新获取Supplier后再执行该方法
        return null;
    }
    // else still us (supplier == this)

    // create new value
    V value = null;
    try {
    	//valueFactory即ProxyClassFactory,通过该BiFunction函数接口获取Class代理对象
        value = Objects.requireNonNull(valueFactory.apply(key, parameter));
    } finally {
        if (value == null) { // remove us on failure
            valuesMap.remove(subKey, this);
        }
    }
    // the only path to reach here is with non-null value
    assert value != null;

    // wrap value with CacheValue (WeakReference)
    //将创建好的Class代理对象封装成CacheValue对象
    CacheValue<V> cacheValue = new CacheValue<>(value);

    // put into reverseMap
    //reverseMap主要用于判断一个类是否是jdk的动态代理以及统计jdk动态代理类数量
    reverseMap.put(cacheValue, Boolean.TRUE);

    // try replacing us with CacheValue (this should always succeed)
    //将CacheValue对象替换Factory对象,之后再代理时就不用再次构建字节码信息并装载了
    if (!valuesMap.replace(subKey, this, cacheValue)) {
        throw new AssertionError("Should not reach here");
    }

    // successfully replaced us with new CacheValue -> return the value
    // wrapped by it
    return value;
}

最终创建Class对象的逻辑处于ProxyClassFactory中,具体如下:

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

            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
            //for循环遍历代理接口做检测,接口匹配对应类加载器、是同一接口
            for (Class<?> intf : interfaces) {
                /*
                 * Verify that the class loader resolves the name of this
                 * interface to the same Class object.
                 */
                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");
                }
                /*
                 * Verify that the Class object actually represents an
                 * interface.
                 */
                if (!interfaceClass.isInterface()) {
                    throw new IllegalArgumentException(
                        interfaceClass.getName() + " is not an interface");
                }
                /*
                 * Verify that this interface is not a duplicate.
                 */
                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;

            /*
             * Record the package of a non-public proxy interface so that the
             * proxy class will be defined in the same package.  Verify that
             * all non-public proxy interfaces are in the same package.
             */
            for (Class<?> intf : interfaces) {
                int flags = intf.getModifiers();
                //如果存在非public接口,则检测代理接口是否位于同一包下,不处于同一包下将抛出异常
                if (!Modifier.isPublic(flags)) {
                    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");
                    }
                }
            }
			
			//默认包使用com.sun.proxy.
            if (proxyPkg == null) {
                // if no non-public proxy interfaces, use com.sun.proxy package
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }

            /*
             * Choose a name for the proxy class to generate.
             */
             //创建代理名,使用$Proxy0,$Proxy1依次类推
            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg + proxyClassNamePrefix + num;

            /*
             * Generate the specified proxy class.
             */
             //使用sun提供的字节码操纵框架构建class的字节码信息
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
            	//使用类加载器装载字节码生成Class对象并返回
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
                /*
                 * A ClassFormatError here means that (barring bugs in the
                 * proxy class generation code) there was some other
                 * invalid aspect of the arguments supplied to the proxy
                 * class creation (such as virtual machine limitations
                 * exceeded).
                 */
                throw new IllegalArgumentException(e.toString());
            }
        }

总结

  • jdk的动态代理类使用了弱缓存,即首次构建Class字节码后会进行缓存之后再次创建代理时则直接使用缓存即可,但由于是若缓存原因代理Class会被JVM根据需要回收,因此被回收后需要再次构建
  • 缓存结构为一个ConcurrentMap<Object, ConcurrentMap<Object, Supplier>> map的二级缓存结构,key为CacheKey类型,其继承了WeakReference,因此会被JVM根据需要回收,同时key作为一级缓存主要用来标记类加载器,因此通过该key获取的一级缓存为指定类加载的二级缓存
  • 二级缓存的key通过类加载器和代理接口共同标记,其类型是new Object()或Key1(代理一个接口),Key2(代理2个接口),KeyX(代理多个接口)均是弱引用类型,因此可以指向固定的一组接口的代理类。
  • 二级缓存的其Value是一个Supplier函数式接口,首次创建一组接口的代理类时由于其字节码未生成因此Supplier=Factory,创建好字节码后Supplier会使用CacheValue封装Class对象替换Factory,Factory和CacheValue都是实现了Supplier接口。
  • 一级缓存的key、二级缓存的key、二级缓存的value(生成字节码后的封装成的CacheValue)均使用了弱引用,其中只有一级缓存使用了ReferenceQueue,因此最终只有一级缓存的引用会被加入到ReferenceQueue中被手动清除。查看二级缓存的key、二级缓存的value的hashCode和equals方法均使用引用对象进行计算,因此同一对象的不同二级缓存key或二级缓存Value其值相等。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值