1.背景
上一節(jié)大致介紹了Proxy動(dòng)態(tài)代理的原理,從幾個(gè)疑問(wèn)上面分析,這一節(jié)介紹一下動(dòng)態(tài)代理的緩存機(jī)制。網(wǎng)上的資源比較少。
可以懷著下面幾個(gè)問(wèn)題閱讀源碼
為什么要緩存
緩存的內(nèi)容是什么
哪里調(diào)用的緩存
緩存的實(shí)現(xiàn)機(jī)制
緩存的過(guò)期機(jī)制
2.屬性說(shuō)明
Proxy涉及的緩存主要在java.lang.reflect.WeakCache這個(gè)類中
該類存在于jdk反射的包中
當(dāng)生成代理對(duì)象的時(shí)候會(huì)用到
java.lang.reflect.Proxy#newProxyInstance
java.lang.reflect.Proxy#getProxyClass0
java.lang.reflect.WeakCache#get
看這個(gè)類的結(jié)構(gòu)
//用了Reference記錄引用隊(duì)列,java gc時(shí)配合清除緩存用
private final ReferenceQueue<K> refQueue = new ReferenceQueue<>();
//緩存的底層實(shí)現(xiàn)
private final ConcurrentMap<Object, ConcurrentMap<Object, Supplier<V>>> map = new ConcurrentHashMap<>();
//記錄所有緩存中的CacheKey,配合緩存的過(guò)期機(jī)制
private final ConcurrentMap<Supplier<V>, Boolean> reverseMap = new ConcurrentHashMap<>();
//兩個(gè)二元操作函數(shù)
private final BiFunction<K, P, ?> subKeyFactory;
private final BiFunction<K, P, V> valueFactory;
這里最重要的的是map,將key-value介紹如下
字段 | 意義 | 備注 |
---|---|---|
key | 通過(guò)classLoader來(lái)進(jìn)行一級(jí)緩存 | 實(shí)際類型為CacheKey |
value | 二級(jí)緩存 | 后續(xù)會(huì)介紹 |
源碼中把這個(gè)value的變量稱為valuesMap,這里順便借用,介紹這個(gè)“二級(jí)緩存”是干嘛的
字段 | 意義 | 備注 |
---|---|---|
key | 二級(jí)緩存的key,由classLoader和interfaces[]標(biāo)識(shí)代理類 | 實(shí)際類型在java.lang.reflect.Proxy.KeyFactory#apply,為Key1或者Key2或者KeyX或者object,取決于代理類實(shí)現(xiàn)的接口數(shù)量 |
value | 用于返回代理類(即Class<?>) | 第一次存儲(chǔ)實(shí)際類型為java.lang.reflect.WeakCache.Factory#Factory,之后取出時(shí),都是java.lang.reflect.WeakCache.CacheValue |
那么為什么要用緩存,緩存的內(nèi)容是什么
緩存就是在動(dòng)態(tài)代理生成代理類時(shí),只用生成一次后面盡量直接復(fù)用
3.實(shí)現(xiàn)機(jī)制
java.lang.reflect.WeakCache#get
public V get(K key, P parameter) {
Objects.requireNonNull(parameter);
expungeStaleEntries();
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
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
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);
}
}
}
}
注意下面幾點(diǎn)
3.1 ConcurrentMap的get以及putIfAbsent的使用
可以看到有幾處代碼,形如
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;
}
}
這個(gè)是為了保證在多線程下,去獲取同一個(gè)key,拿到的是同一個(gè)引用對(duì)象
可以理解像單例模式里面要判斷兩次,來(lái)保證不會(huì)多次生成對(duì)象
if(instance==null) {
synchronize(XXX.class) {
if(instance==null){
instance = new XXX();
}
}
}
可以參考http://wxl24life.iteye.com/blog/1746794
3.2 為什么說(shuō)第一次valuesMap里面value的實(shí)際類型和之后不一樣
根據(jù)Supplier<V> supplier = valuesMap.get(subKey);中supplier值以及在while循環(huán)第幾次,按照時(shí)間先后考慮
3.2.1 supplier==null,第一次while循環(huán)
這種出現(xiàn)在第一次請(qǐng)求中,二級(jí)緩存沒(méi)有該key
邏輯為
supplier = valuesMap.putIfAbsent(subKey, factory);
if (supplier == null) {
// successfully installed Factory
supplier = factory;
}
此時(shí)valuesMap里面的key是subKey
subKey是
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
根據(jù)java.lang.reflect.Proxy.KeyFactory#apply
subKey實(shí)際類型是Key1,Key2,Object或者KeyX,根據(jù)實(shí)現(xiàn)的接口數(shù)量決定
private static final class KeyFactory
implements BiFunction<ClassLoader, Class<?>[], Object>
{
@Override
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);
}
}
}
3.2.2 supplier==null,第二次while循環(huán)(或者因其他原因,第n次while循環(huán))
這種出現(xiàn)在第一次請(qǐng)求中,二級(jí)緩存沒(méi)有該key,supplier被賦值之后
邏輯為
if (supplier != null) {
// supplier might be a Factory or a CacheValue<V> instance
V value = supplier.get();
if (value != null) {
return value;
}
}
supplier在第一次循環(huán)時(shí),已經(jīng)為factory了,這里調(diào)用get方法,就是進(jìn)入
java.lang.reflect.WeakCache.Factory#get的邏輯
@Override
public synchronized V get() { // serialize access
// re-check
Supplier<V> supplier = valuesMap.get(subKey);
if (supplier != this) {
//supplier和當(dāng)前supplier不等,驗(yàn)證不正確
return null;
}
V value = null;
try {
//生成代理類對(duì)應(yīng)的Class<?>信息
value = Objects.requireNonNull(valueFactory.apply(key, parameter));
} finally {
if (value == null) { // remove us on failure
valuesMap.remove(subKey, this);
}
}
assert value != null;
CacheValue<V> cacheValue = new CacheValue<>(value);
//把之前二級(jí)緩存的value替換為當(dāng)前的CacheValue
if (valuesMap.replace(subKey, this, cacheValue)) {
// put also in reverseMap
reverseMap.put(cacheValue, Boolean.TRUE);
} else {
throw new AssertionError("Should not reach here");
}
return value;
}
其中, value = Objects.requireNonNull(valueFactory.apply(key, parameter));就是代理類
java.lang.reflect.Proxy.ProxyClassFactory#apply //返回Class<?>
//具體生成細(xì)節(jié)參考上一節(jié)
在生成value之后
//把當(dāng)前的值即this(類型為Factory)替換為了cacheValue(類型CacheValue)
valuesMap.replace(subKey, this, cacheValue)
并且這一次get()最終返回的是value;也就是Class<?>
3.2.3 supplier!=null
出現(xiàn)在之后的某一次請(qǐng)求中,二級(jí)緩存已經(jīng)有該key-value
if (supplier != null) {
// supplier might be a Factory or a CacheValue<V> instance
V value = supplier.get();
if (value != null) {
return value;
}
}
此時(shí),supplier的類型為CacheValue
在Factory#get中生成時(shí),就是
CacheValue<V> cacheValue = new CacheValue<>(value);
CacheValue#get方法,最終轉(zhuǎn)入java.lang.ref.Reference#get,返回的就是這個(gè)value,即構(gòu)造函數(shù)的參數(shù)value
3.3緩存的過(guò)期機(jī)制
這里用了java.lang.reflect.WeakCache.CacheKey是繼承WeakReference的
即弱引用,在java gc中,滿足適當(dāng)?shù)臈l件時(shí),當(dāng)一個(gè)referent是ROOT可達(dá)且為弱引用時(shí),會(huì)將其放入對(duì)應(yīng)的referenceQueue。
核心函數(shù)在
private void expungeStaleEntries() {
CacheKey<K> cacheKey;
while ((cacheKey = (CacheKey<K>)refQueue.poll()) != null) {
cacheKey.expungeFrom(map, reverseMap);
}
}
可能會(huì)奇怪這里并沒(méi)有refQueue的enqueue操作,這個(gè)操作是java gc的時(shí)候做的,這里不深究。
只要知道gc時(shí),某些場(chǎng)景會(huì)進(jìn)行refQueue的enqueue即可。
那么,之后再調(diào)用expungeStaleEntries()就能清除過(guò)期的緩存。
4. 思考
4.1 為什么要二級(jí)緩存
一級(jí)緩存用來(lái)區(qū)分classLoader,二級(jí)緩存用來(lái)區(qū)分實(shí)現(xiàn)的接口
生成類的緩存是按照ClassLoader來(lái)劃分的,這是因?yàn)轭惖膮^(qū)分不僅根據(jù)類名還根據(jù)裝載類的ClassLoader,也就是說(shuō)同一個(gè)類被不同的ClassLoader加載,那么它們也是不同的.
參考
http://www.cnblogs.com/cruze/p/3843996.html
http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-5.html#jvms-5.3
4.2 ConcurrentHashMap想要獲取SingleValue的方法(嚴(yán)格來(lái)說(shuō)是同一個(gè)引用)
如果putIfAbsent返回值不為null,那么返回該值,不要重新put
http://wxl24life.iteye.com/blog/1746794
4.3 二級(jí)緩存valueMaps的value的類型不一樣
第一次是Factory,調(diào)用get()時(shí),生成代理類信息之后,將CacheValue作為值替換了當(dāng)前key對(duì)應(yīng)的value
之后調(diào)用,都是CacheValue.get()返回java.lang.ref.Reference#get,最終就是CacheValue的構(gòu)造函數(shù)參數(shù),即代理類信息Class<?>
4.4 一級(jí)緩存的key-value與二級(jí)緩存的key-value
4.5 在'背景'中提出的問(wèn)題,在上文中都有解釋
4.6 緩存的過(guò)期機(jī)制
利用java.lang.reflect.WeakCache.CacheKey繼承WeakReference,配合ReferenceQueue使用。
在java gc時(shí)某些條件下觸發(fā)。
5.暫時(shí)不明白的問(wèn)題
5.1 reverseMap的value有什么用?
感覺(jué)純粹是利用ConcurrentHashMap的"Concurrent"的特性,value并沒(méi)有什么用,代碼里面并沒(méi)有處理里面的value,都是Boolean.TRUE;
5.2 實(shí)現(xiàn)二級(jí)緩存的邏輯為什么要這么寫,不能一步到位嗎?
為什么先放入Factory,調(diào)用Factory.get時(shí)進(jìn)行replace操作再放入CacheValue,不能一步到位寫入CacheValue后面直接用嗎
6. refer
concurrentHashMap 的putIfAbsent完成single value
http://wxl24life.iteye.com/blog/1746794
cglib的緩存說(shuō)明
http://www.cnblogs.com/cruze/p/3843996.html
http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-5.html#jvms-5.3
gc 與 reference
http://www.cnblogs.com/maxmys/p/3998842.html
http://hongjiang.info/java-referencequeue/
http://zhang-xzhi-xjtu.iteye.com/blog/413159