JDK動態代理代理類的生成與緩存

JDK動態代理代理類的生成與緩存

一、緩存相關的類及主要結構

代理類的生成與緩存主要在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.cnblogs.com/James-Gong/p/8126129.html

https://www.jianshu.com/p/9f5566b5e7fb

https://www.jianshu.com/p/2326a397802c

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章