一、緩存相關的類及主要結構
代理類的生成與緩存主要在java.lang.reflect.WeakCache<K, P, V>這個類中完成,此類用於代理類緩存的主要結構如下
// 用了Reference記錄引用隊列,java gc時配合清除緩存用(本文不做深究) private final ReferenceQueue<K> refQueue = new ReferenceQueue<>(); // 用於對代理類進行緩存的map,其中key為一級緩存的鍵,值為二級緩存map private final ConcurrentMap<Object, ConcurrentMap<Object, Supplier<V>>> map = new ConcurrentHashMap<>(); // 記錄所有緩存中的CacheKey,配合緩存的過期機制(本文不做深究) private final ConcurrentMap<Supplier<V>, Boolean> reverseMap = new ConcurrentHashMap<>(); // 兩個二元操作函數(第一個是二級緩存的key的工廠,第二個是二級緩存值的工廠) private final BiFunction<K, P, ?> subKeyFactory; private final BiFunction<K, P, V> 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<Object>() { 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<ClassLoader, Class<?>[], 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<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 // 生成二級緩存key Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter)); // 通過key獲取二級緩存value,即緩存的代理類。不存在則新建代理類並加入緩存。 Supplier<V> supplier = valuesMap.get(subKey); Factory factory = null; while (true) { if (supplier != null) { // supplier might be a Factory or a CacheValue<V> 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<V> instance V value = supplier.get(); if (value != null) { return value; } }
這里調用的get方法就是factory 的get方法,在get方法中會用真正的緩存代理類替換緩存中的factory
public synchronized V get() { // serialize access // re-check // 獲取當前二級緩存 Supplier<V> 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<V> 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<V> 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<V> 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<V> 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<T> { 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