一、缓存相关的类及主要结构
代理类的生成与缓存主要在java.lang.reflect.WeakCache<K, P, V>这个类中完成,此类用于代理类缓存的主要结构如下
// 用了Reference记录引用队列,java gc时配合清除缓存用(本文不做深究)private final ReferenceQueuerefQueue = new ReferenceQueue<>();// 用于对代理类进行缓存的map,其中key为一级缓存的键,值为二级缓存mapprivate final ConcurrentMap
这里最核心的是用于缓存的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
其中这一步 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
上述操作
在生成一级缓存时用到了类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 CacheValueinstance V value = supplier.get(); if (value != null) { return value; } }
这里调用的get方法就是factory 的get方法,在get方法中会用真正的缓存代理类替换缓存中的factory
public synchronized V get() { // serialize access // re-check // 获取当前二级缓存 Suppliersupplier = 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 CacheValueinstance 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方法执行逻辑
Suppliersupplier = valuesMap.get(subKey); // supplier为cacheValue对象 if (supplier != this) { // this 为factory return null; }
2.当缓存中存在所需代理类时。
只需要执行一次循环,循环中的代码逻辑为
if (supplier != null) { // supplier might be a Factory or a CacheValueinstance 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