設計模式(1-3)-動態代理(WeakCache的運用)
閱讀本篇文章前,請事先閱讀 理解Java的強引用、軟引用、弱引用和虛引用。 看看什麼是強引用、什麼是弱引用及它們的用途,很必要!!!
上一節講到,獲取對應的代理類時,首先會從緩存中去拿,若拿不到才會去生成。實現緩存的儲存,如何根據指定值拿到緩存都是由WeakCache這個類實現的。
我們先去探究一下WeakCache~
一、WeakCache
WeakCache有兩級緩存,它的鍵值對: (key, sub-key) -> value。 一級緩存的keys和values是弱引用, 二級緩存的sub-keys是強引用。
sub-keys, 根據keys和parameters使用subKeyFactory(構造器傳入)計算出的。 Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
values, 跟獲取sub-keys類似,但是它是使用valueFactory(構造器傳入)。 value = Objects.requireNonNull(valueFactory.apply(key, parameter));
為啥要使用WeakCache作為動態代理的緩存,我在網上看到了一個文章,What is the use case of WeakCache in Java? [closed], 可以將裏面的圖片對象 類比 生成的代理類(都要佔用較大的內存),也不知正確與否(JVM早日把你幹掉!!!)
我認為的原因是,
- 生成的代理類佔用內存較大,key(弱引用, GC時會被回收)失效時, 可以被及時處理(
expungeStaleEntries()
就是處理key失效時,清楚掉對應的value的方法,在get
、containsValue
,size
被調用時調用)
簡而言之,為了能用到時隨時能用到,但是不影響GC,畢竟內存很寶貴的
1. 變量與構造器
// 弱引用被回收時,將被添加到這個引用隊列中
private final ReferenceQueue<K> refQueue
= new ReferenceQueue<>();
// the key type is Object for supporting null key
private final ConcurrentMap<Object, ConcurrentMap<Object, Supplier<V>>> map
= new ConcurrentHashMap<>();
// 保存value,當獲取緩存的size時,就比較方便得到
private final ConcurrentMap<Supplier<V>, Boolean> reverseMap
= new ConcurrentHashMap<>();
// 生成subKey及value的類
private final BiFunction<K, P, ?> subKeyFactory;
private final BiFunction<K, P, V> valueFactory;
構造器:
java.lang.reflect.WeakCache#WeakCache
public WeakCache(BiFunction<K, P, ?> subKeyFactory,
BiFunction<K, P, V> valueFactory) {
this.subKeyFactory = Objects.requireNonNull(subKeyFactory);
this.valueFactory = Objects.requireNonNull(valueFactory);
}
2. 重要的方法
2.1 get()!!!
結合java.lang.reflect.Proxy#getProxyClass0
使用到的WeakCache.get
方法,我們看看其get()
的原理
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
...
// 從這開始
return proxyClassCache.get(loader, interfaces);
}
java.lang.reflect.Proxy#proxyClassCache
/**
* a cache of proxy classes
*/
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
java.lang.reflect.Proxy.KeyFactory
,根據實現接口個數返回不同的key, 有興趣的同學可以去看看。
java.lang.reflect.Proxy.ProxyClassFactory
, 上節講過的,若指定的參數的緩存失效,就會使用該工廠類,生成對應的代理類。
tips: Supplier, 是一個Java8提供的一個函數式接口,結果的提供者,其get
方法會返回一個結果。
有了以上的知識,我們一起看看WeakCache
的get
實現吧
java.lang.reflect.WeakCache#get
: 👇
public V get(K key, P parameter) {
Objects.requireNonNull(parameter);
// 清理掉被GC的緩存
expungeStaleEntries();
// 將key包裝成弱引用
Object cacheKey = CacheKey.valueOf(key, refQueue);
// 通過cachekey獲取二級緩存 sub-keys - values
ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
// 驗證二級緩存valuesMap是否為null, 為null就初始化;
// 還有情況可能就是在初始化過程中,其他線程已經將它初始化,若這樣,將實例指向第一次初始化的實例
if (valuesMap == null) {
ConcurrentMap<Object, Supplier<V>> oldValuesMap
= map.putIfAbsent(cacheKey,
valuesMap = new ConcurrentHashMap<>());
if (oldValuesMap != null) {
valuesMap = oldValuesMap;
}
}
// subKeyFactory = KeyFactory, 此時就是根據實現的接口個數返回不同的對象
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
// 第一次,valuesMap才被初始化,所以supplier為null
Supplier<V> supplier = valuesMap.get(subKey);
Factory factory = null;
// 下面是一個輪詢,直到拿到不為null的supplier且supplier.get()不為null為止
while (true) {
if (supplier != null) {
// 第一次進來,supplier應該是Factory;
// 第二次,獲取的對應的參數未變的話,從valuesMap中獲取到的supplier就是一個CacheValue<V>的實例了,此時就是從緩存中獲取的了
// 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)
// 懶加載
if (factory == null) {
factory = new Factory(key, parameter, subKey, valuesMap);
}
if (supplier == null) {
//
supplier = valuesMap.putIfAbsent(subKey, factory);
if (supplier == null) {
// 一路成功的話,這裡就會運行完畢,進行下一個循環,獲取到值就直接返回value了
supplier = factory;
}
// supplier賦值的過程中,被其他線程提前賦值了, 繼續循環
} else {
// 替換以前的supplier
if (valuesMap.replace(subKey, supplier, factory)) {
// successfully replaced
// cleared CacheEntry / unsuccessful Factory
// with our Factory
supplier = factory;
} else {
// 替換失敗就使用目前的supplier,重試
supplier = valuesMap.get(subKey);
}
}
}
}
我們去看看實現Suppiler
的Factory
是如何提供返回結果的
java.lang.reflect.WeakCache.Factory
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) {
// 在我們等待時發生了改變:
// 1. 被一個CacheValue替換了
// 2. were removed because of failure
// 此時,就返回null, 讓WeakCache.get 繼續循環
return null;
}
// else still us (supplier == this)
// create new value
V value = null;
try {
// !! 此時的valueFactory就是ProxyClassFactory; 這裡會去生成代理類
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);
// 將cacheValue保存起來
reverseMap.put(cacheValue, Boolean.TRUE);
// 用cacheValue替換原有的值
// try replacing us with CacheValue (this should always succeed)
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;
}
}
對於java.lang.reflect.WeakCache#get
,java.lang.reflect.WeakCache.Factory#get
源碼的一個總結:
- 若
valuesMap
找不到subKey
對應的supplier, 此時supplier是Factory
的實例,調用supplier.get()
的時候就去調用java.lang.reflect.WeakCache.Factory#get
- 若
valuesMap
有subKey
對應的supplier,此時supplier就是CacheValue的實例
我們來看看supplier是CacheValue
是,調用supplier.get()
,實際調用的哪
java.lang.reflect.WeakCache#get
按f7,step into ->
java.lang.ref.Reference#get
我們再來看看,CacheValue
的結構
private static final class CacheValue<V>
extends WeakReference<V> implements Value<V>
{
private final int hash;
CacheValue(V value) {
// 點super進去看看,你就會知道了!!!
super(value);
this.hash = System.identityHashCode(value); // compare by identity
}
...
}
java.lang.reflect.WeakCache.Value
private interface Value<V> extends Supplier<V> {}
我們沒有看到CacheValue
中實現Supplier
的get()
, 但是CacheValue
的父類的父類Reference
早已提供了get()
這個方法,返回代理類的對象。
難道Supplier
與Reference
是天作之合? 希望我慢慢欣賞代碼的美~
二、總結
結合了java.lang.reflect.Proxy#getProxyClass0
中使用到WeakCache
的地方,講了講,緩存獲取(get
)的整個過程。