设计模式(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 = https://www.it610.com/article/Objects.requireNonNull(valueFactory.apply(key, parameter));
【设计模式(1-3)-动态代理(WeakCache的运用)】为啥要使用WeakCache作为动态代理的缓存,我在网上看到了一个文章,What is the use case of WeakCache in Java? [closed], 可以将里面的图片对象 类比 生成的代理类(都要占用较大的内存),也不知正确与否(JVM早日把你干掉!!!)
我认为的原因是,

  1. 生成的代理类占用内存较大,key(弱引用, GC时会被回收)失效时, 可以被及时处理(expungeStaleEntries()就是处理key失效时,清楚掉对应的value的方法,在getcontainsValue,size被调用时调用)
简而言之,为了能用到时随时能用到,但是不影响GC,毕竟内存很宝贵的
1. 变量与构造器
// 弱引用被回收时,将被添加到这个引用队列中 private final ReferenceQueue refQueue = new ReferenceQueue<>(); // the key type is Object for supporting null key private final ConcurrentMap>> map = new ConcurrentHashMap<>(); // 保存value,当获取缓存的size时,就比较方便得到 private final ConcurrentMap, Boolean> reverseMap = new ConcurrentHashMap<>(); // 生成subKey及value的类 private final BiFunction subKeyFactory; private final BiFunction valueFactory;

构造器:
java.lang.reflect.WeakCache#WeakCache
public WeakCache(BiFunction subKeyFactory, BiFunction 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[], Class> proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

java.lang.reflect.Proxy.KeyFactory,根据实现接口个数返回不同的key, 有兴趣的同学可以去看看。
java.lang.reflect.Proxy.ProxyClassFactory, 上节讲过的,若指定的参数的缓存失效,就会使用该工厂类,生成对应的代理类。
tips: Supplier, 是一个Java8提供的一个函数式接口,结果的提供者,其get方法会返回一个结果。
有了以上的知识,我们一起看看WeakCacheget实现吧
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> valuesMap = map.get(cacheKey); // 验证二级缓存valuesMap是否为null, 为null就初始化; // 还有情况可能就是在初始化过程中,其他线程已经将它初始化,若这样,将实例指向第一次初始化的实例 if (valuesMap == null) { ConcurrentMap> 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 supplier = valuesMap.get(subKey); Factory factory = null; // 下面是一个轮询,直到拿到不为null的supplier且supplier.get()不为null为止 while (true) { if (supplier != null) { // 第一次进来,supplier应该是Factory; // 第二次,获取的对应的参数未变的话,从valuesMap中获取到的supplier就是一个CacheValue的实例了,,此时就是从缓存中获取的了 // supplier might be a Factory or a CacheValue instance V value = https://www.it610.com/article/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); } } } }

我们去看看实现SuppilerFactory是如何提供返回结果的
java.lang.reflect.WeakCache.Factory
private final class Factory implements Supplier {private final K key; private final P parameter; private final Object subKey; private final ConcurrentMap> valuesMap; Factory(K key, P parameter, Object subKey, ConcurrentMap> valuesMap) { this.key = key; this.parameter = parameter; this.subKey = subKey; this.valuesMap = valuesMap; }@Override public synchronized V get() { // serialize access // re-check Supplier 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 = https://www.it610.com/article/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 cacheValue = https://www.it610.com/article/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源码的一个总结:
  1. valuesMap找不到subKey对应的supplier, 此时supplier是Factory的实例,调用supplier.get()的时候就去调用java.lang.reflect.WeakCache.Factory#get
  2. valuesMapsubKey对应的supplier,此时supplier就是CacheValue的实例
    我们来看看supplier是CacheValue是,调用supplier.get(),实际调用的哪
java.lang.reflect.WeakCache#get
设计模式(1-3)-动态代理(WeakCache的运用)
文章图片

按f7,step into ->
java.lang.ref.Reference#get
设计模式(1-3)-动态代理(WeakCache的运用)
文章图片

我们再来看看,CacheValue的结构
private static final class CacheValue extends WeakReference implements Value { 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 extends Supplier {}

我们没有看到CacheValue中实现Supplierget(), 但是CacheValue的父类的父类Reference早已提供了get()这个方法,返回代理类的对象。
难道SupplierReference是天作之合? 希望我慢慢欣赏代码的美~
二、总结 结合了java.lang.reflect.Proxy#getProxyClass0中使用到WeakCache的地方,讲了讲,缓存获取(get)的整个过程。
三、参考
  1. 理解Java的强引用、软引用、弱引用和虚引用
  2. What is the use case of WeakCache in Java? [closed]

    推荐阅读