博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JDK动态代理代理类的生成与缓存
阅读量:6827 次
发布时间:2019-06-26

本文共 12364 字,大约阅读时间需要 41 分钟。

一、缓存相关的类及主要结构

代理类的生成与缓存主要在java.lang.reflect.WeakCache<K, P, V>这个类中完成,此类用于代理类缓存的主要结构如下

// 用了Reference记录引用队列,java gc时配合清除缓存用(本文不做深究)private final ReferenceQueue
refQueue = new ReferenceQueue<>();// 用于对代理类进行缓存的map,其中key为一级缓存的键,值为二级缓存mapprivate final ConcurrentMap
>> map = new ConcurrentHashMap<>();// 记录所有缓存中的CacheKey,配合缓存的过期机制(本文不做深究)private final ConcurrentMap
, Boolean> reverseMap = new ConcurrentHashMap<>();// 两个二元操作函数(第一个是二级缓存的key的工厂,第二个是二级缓存值的工厂)private final BiFunction
subKeyFactory;private final BiFunction
valueFactory;

这里最核心的是用于缓存的map,其中key-value关系如下:

字段 意义 备注
key 一级缓存的key,由类加载器classLoader决定的 类型为 java.lang.reflect.WeakCache.CacheKey.valueOf(K, ReferenceQueue<K>)
value 一级缓存value,实际是二级缓存map 类型为 java.util.concurrent.ConcurrentMap<Object, Supplier<V>>

源码中把这个value的变量称为valuesMap,valuesMap是代理类二级缓存,其中key-value关系如下:

字段 意义 备注
key 二级缓存key,由classLoader和interfaces[]标识代理类

类型为 java.lang.reflect.Proxy.KeyFactory.apply(ClassLoader, Class<?>[]),

实际值为Object或者Key1或者Key2或者KeyX,取决于代理类实现的接口数量

value 二级缓存value,即需要的代理类Class<?>)

类型为 java.lang.reflect.WeakCache.Supplier<V>,

第一次存储实际类型为java.lang.reflect.WeakCache.Factory.Factory(K, P, Object, ConcurrentMap<Object, Supplier<V>>),之后取出时,都是java.lang.reflect.WeakCache.CacheValue.CacheValue(V)。具体实现机制后面会介绍

二、具体实现

当用java.lang.reflect.Proxy类生成代理类实例时会用到类中的newProxyInstance方法,源码如下

@CallerSensitive    public static Object newProxyInstance(ClassLoader loader,                                          Class
[] interfaces, InvocationHandler h) throws IllegalArgumentException { if (h == null) { throw new NullPointerException(); } final Class
[] intfs = interfaces.clone(); final SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } /* * Look up or generate the designated proxy class. */   // 从缓存获取或重新生成需要的代理类 Class
cl = getProxyClass0(loader, intfs); /* * Invoke its constructor with the designated invocation handler. */ try {   // 通过构造器创建代理对象实例 final Constructor
cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) { // create proxy instance with doPrivilege as the proxy class may // implement non-public interfaces that requires a special permission return AccessController.doPrivileged(new PrivilegedAction() { public Object run() { return newInstance(cons, ih); } }); } else { return newInstance(cons, ih); } } catch (NoSuchMethodException e) { throw new InternalError(e.toString()); } }
其中这一步 Class
cl = getProxyClass0(loader, intfs) 就是从缓存获取或重新生成需要的代理类的方法;
getProxyClass0也是java.lang.reflect.Proxy类中的方法,源码如下
private static Class
getProxyClass0(ClassLoader loader, Class
... interfaces) {   // 校验接口数量 if (interfaces.length > 65535) { throw new IllegalArgumentException("interface limit exceeded"); }   // 从缓存获取代理类 return proxyClassCache.get(loader, interfaces); }

这里的变量proxyClassCache是Proxy类的的一个静态私有成员变量,它的类型就是上面提到的类java.lang.reflect.WeakCache<K, P, V>。

注意,此处初始化WeakCache用到了Proxy的两个内部类java.lang.reflect.Proxy.KeyFactory和java.lang.reflect.Proxy.ProxyClassFactory,这两个类后续会讲到。

/**     * a cache of proxy classes     */    private static final WeakCache
[], Class
> proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
下面就对通过方法java.lang.reflect.WeakCache.get(K, P)的源码,探究其具体实现
public V get(K key, P parameter) {        Objects.requireNonNull(parameter);        expungeStaleEntries();      // 通过类加载器classLoader生成以及一级缓存key        Object cacheKey = CacheKey.valueOf(key, refQueue);        // lazily install the 2nd level valuesMap for the particular cacheKey      // 获取二级缓存,不存在则新建        ConcurrentMap
> valuesMap = map.get(cacheKey); if (valuesMap == null) { ConcurrentMap
> oldValuesMap = map.putIfAbsent(cacheKey, valuesMap = new ConcurrentHashMap<>()); if (oldValuesMap != null) { valuesMap = oldValuesMap; } } // create subKey and retrieve the possible Supplier
stored by that // subKey from valuesMap   // 生成二级缓存key Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));   // 通过key获取二级缓存value,即缓存的代理类。不存在则新建代理类并加入缓存。 Supplier
supplier = valuesMap.get(subKey); Factory factory = null; while (true) { if (supplier != null) { // supplier might be a Factory or a CacheValue
instance 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 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 { 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); } } }

上述操作

在生成一级缓存时用到了类java.lang.reflect.WeakCache.CacheKey的方法valueOf(K, ReferenceQueue<K>),其唯一性由类加载器决定。

在生成二级缓存时用到了类java.lang.reflect.WeakCache<K, P, V>的成员变量subKeyFactory的apply方法,该成员变量在实例化时的实际类型是java.lang.reflect.Proxy.KeyFactory,该apply方法的源码如下:

   // 根据接口数量生成二级缓存key        public Object apply(ClassLoader classLoader, Class
[] interfaces) { 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); } }

想了解Key1,Key2,KeyX的底层实现可以去看其源码,三个类都是java.lang.reflect.Proxy类的内部类。key0就是一个Object对象。

在最后一步:通过key获取二级缓存value可能不太容易理解。且前文提到过二级缓存的值,第一次存储实际类型为java.lang.reflect.WeakCache.Factory.Factory(K, P, Object, ConcurrentMap<Object, Supplier<V>>),之后取出时,都是java.lang.reflect.WeakCache.CacheValue.CacheValue(V)。这里通过代码执行顺序对具体实现进行探究

 

1.当缓存中不存在所需代理类时

在进入循环前supplier=null, factory=null,

第一次循环的逻辑为

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

执行完之后,将新建的factory存入了二级缓存。valuesMap.get(subKey) = supplier = factory = new Factory(key, parameter, subKey, valuesMap);

第二次循环的逻辑为

if (supplier != null) {                // supplier might be a Factory or a CacheValue
instance V value = supplier.get(); if (value != null) { return value; } }

这里调用的get方法就是factory 的get方法,在get方法中会用真正的缓存代理类替换缓存中的factory

public synchronized V get() { // serialize access            // re-check            // 获取当前二级缓存            Supplier
supplier = valuesMap.get(subKey); //supplier和当前supplier不等,验证不正确(线程并发时用到) if (supplier != this) { return null; } // else still us (supplier == this) // create new value V value = null; try {    // 生成代理类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)    // 生成代理类缓存对象 CacheValue
cacheValue = new CacheValue<>(value); // try replacing us with CacheValue (this should always succeed)    // 用新的缓存对象替换旧的(当缓存对象是factory时用cacheValue替换factory) if (valuesMap.replace(subKey, this, cacheValue)) { // put also in reverseMap reverseMap.put(cacheValue, Boolean.TRUE); } else { throw new AssertionError("Should not reach here"); } // successfully replaced us with new CacheValue -> return the value // wrapped by it return value; }

注意在多线程的情况会有如下一种特殊情况

当线程一执行完了第一次循环。cpu的使用权被线程二获得。此时线程二刚刚执行到方法java.lang.reflect.WeakCache.get(K, P)中--通过key获取二级缓存value这一步,执行后将有两个线程都将进入都循环,且两个线程的同时满足valuesMap.get(subKey) = supplier = factory = new Factory(key, parameter, subKey, valuesMap);且由于factory的get方法是线程同步的

这时先进入循环的线程代码执行逻辑与上述第二次循环的逻辑一样。后进入循环的线程,代码执行顺序则会有所不同。

循环中代码逻辑

       if (supplier != null) {                // supplier might be a Factory or a CacheValue
instance V value = supplier.get(); // 这里返回值为null if (value != null) { // value == null,则不会执行return,代码继续向下执行 return value; } } if (supplier == null) { // 此处代码省略 } else { // 由于supplier == factory 不为null,执行else中的代码 if (valuesMap.replace(subKey, supplier, factory)) { // 此处supplier为factory,valuesMap.get(subKey)为cacheValue,无法替换,返回false // successfully replaced // cleared CacheEntry / unsuccessful Factory // with our Factory supplier = factory; } else { // retry with current supplier supplier = valuesMap.get(subKey); // 取得缓存中的cacheValue赋值给suppplier,下次循环时,返回正确的cacheValue } }

factory 的get方法执行逻辑

Supplier
supplier = valuesMap.get(subKey); // supplier为cacheValue对象 if (supplier != this) { // this 为factory return null; }

2.当缓存中存在所需代理类时。

只需要执行一次循环,循环中的代码逻辑为

if (supplier != null) {                // supplier might be a Factory or a CacheValue
instance V value = supplier.get(); if (value != null) { return value; } }

这里的get方法是cacheValue 的get方法,又由于java.lang.reflect.WeakCache.CacheValue.CacheValue(V)有一个超类java.lang.ref.Reference<T>中定义了一个public的get方法,因此实际调用的是该超类的get方法,返回实际的缓存代理类。

public abstract class Reference
{ private T referent; /* Treated specially by GC */ public T get() { return this.referent; }} 

 

三、相关知识点

1.concurrentMap的get、putIfAbsent、replacea的使用

2.java多态的相关特性:当子类继承了一个父类和实现了一个接口时,若父类和接口中有同名的public方法,则子类可以不用实现接口中的方法,直接继承父类的同名方法即可。注意:接口中的方法都是public abstract的,因此父类中的方法只有是public的时子类才不需要重写接口中的方法,父类方法不是public的则子类还是需要重写接口中的方法。 

 3.双重检查,解决延迟初始化的竞态条件"检查-初始化"。第一次生成代理类的时候存在一个延迟初始化的竞态条件。这里为了保证线程安全,第一次生成代理类时需要线程同步以保证线程安全,后续获取代理类则不需要以减轻并发压力,因此引入了生成二级缓存时引入了Factory类。

本文参考:https://www.jianshu.com/p/9f5566b5e7fb

https://www.jianshu.com/p/2326a397802c

 

 

 

转载于:https://www.cnblogs.com/James-Gong/p/8126129.html

你可能感兴趣的文章
互联网
查看>>
MySQL load data 权限相关
查看>>
ScriptManager.RegisterStartupScript失效的解决方案
查看>>
vsftpd 添加用户
查看>>
递归方法
查看>>
Sonar+maven+jenkins集成,Java代码走查
查看>>
js中点击返回顶部
查看>>
Gtest源码剖析:1.实现一个超级简单的测试框架xtest
查看>>
第三方模块的安装
查看>>
Terracotta中锁与性能的问题
查看>>
遇到Linux系统安装时窗口过大,按钮点不到,该怎么解决
查看>>
js 判断输入是否为正整数
查看>>
「收藏」一些有趣的图
查看>>
探索虚函数(二)
查看>>
李青云老人的长寿秘诀【转】
查看>>
Springboot Thymeleaf 发邮件 将html内容展示在邮件内容中
查看>>
BZOJ2434:[NOI2011]阿狸的打字机——题解
查看>>
第5件事 做一个有taste的产品人
查看>>
暂时记录
查看>>
MicroPython开发之物联网快速开发板
查看>>