dubbo學習篇之dubbo內核解析(三)Wrapper

dubbo的內核

dubbo所有功能都是基於dubbo內核之上完成的,dubbo內核由四部分構成,分別爲SPI,Adaptive,Wrapper,Activate。
而dubbo的內核設計原則,也是我們所熟悉的aop,ioc與動態編譯compiler,這些稱之爲dubbo的內核原理。

Wrapper

Wrapper機制,即擴展點自動包裝。Wrapper 類同樣實現了擴展點接口,但是 Wrapper 不是擴展點的真正實現。它的用途主要是用於從 ExtensionLoader 返回擴展點時,包裝在真正的擴展點實現外。即從 ExtensionLoader 中返回的實際上是 Wrapper 類的實例,Wrapper 持有了實際的擴展點實現類。
擴展點的 Wrapper 類可以有多個,也可以根據需要新增。
通過 Wrapper 類可以把所有擴展點公共邏輯移至 Wrapper 中。新加的 Wrapper 在所有的擴展點上添加了邏輯,有些類似 AOP,即 Wrapper 代理了擴展點。

Wrapper的規範

Wrapper 機制不是通過註解實現的,而是通過一套 Wrapper 規範實現的。
Wrapper 類在定義時需要遵循如下規範。

  • 該類要實現 SPI 接口
  • 該類中要有 SPI 接口的引用
  • 該類中必須含有一個含參的構造方法且參數只能有一個類型爲SPI藉口
  • 在接口實現方法中要調用 SPI 接口引用對象的相應方法
  • 該類名稱以 Wrapper 結尾

Wrapper的代碼實現

構建Wraaper類

package com.demo.adaptive.service.impl;

import com.demo.adaptive.service.Order;
import org.apache.dubbo.common.URL;

public class OrderWrapper implements Order {

    private Order order;

    // 這個構造方法是dubbo唯一判斷該類是否是Wrapper類的依據
    public OrderWrapper(Order order) {
        this.order = order;
    }

    @Override
    public void pay(URL url) {
        System.out.println("before");
        order.pay(url);
        System.out.println("after");
    }
}

在服務提供者文件中定義Wrapper類

wrapper=com.demo.adaptive.service.impl.OrderWrapper

執行Main函數得到以下結果
在這裏插入圖片描述
如果需要多個Wrapper只需要定義多個符合規範的類,並填寫在服務提供者文件上即可,但是需要注意,執行的順序爲後添加的先執行

Wrapper源碼解析

Wrapper功能實現分爲兩個部分,一個是加載Extension時會把Wrapper類放入緩存中,另一部分取得服務提供者實例時,將裝配過的Wrapper類返回。

加載Wrapper類

前面我們已經分析過了源碼執行的流程,下面我只貼關鍵部分的代碼

        // org.apache.dubbo.common.extension.ExtensionLoader 729-731
        // 判斷該類是否是Wrapper類
        } else if (isWrapperClass(clazz)) {
            // 如果是Wrapper類,則經該類放入緩存
            cacheWrapperClass(clazz);
        }
    /**
     * 測試該類是否是一個Wrapper類
     * <p>
     * which has Constructor with given class type as its only argument
     */
    private boolean isWrapperClass(Class<?> clazz) {
        try {
            // 嘗試取得參數類型爲SPI接口類型的構造函數
            clazz.getConstructor(type);
            // 存在該類型的構造函數則爲Wrapper類
            return true;
        } catch (NoSuchMethodException e) {
            // 不存在則不是Wrapper類
            return false;
        }
    }
    /**
     * 緩存Wrapper類
     * <p>
     * like: ProtocolFilterWrapper, ProtocolListenerWrapper
     */
    private void cacheWrapperClass(Class<?> clazz) {
        // 維護一個線程安全的HashSet來存放Wrapper
        // ExtensionLoader.getExtensionLoader(Order.class)
        // 通過上面取得ExtensionLoader的代碼你需要知道,每一個SPI接口都有一個ExtensionLoader,所以這裏面的緩存也是每一個SPI接口都有他的Wrapper緩存,生命週期和loader的生命週期一致
        if (cachedWrapperClasses == null) {
            cachedWrapperClasses = new ConcurrentHashSet<>();
        }
        cachedWrapperClasses.add(clazz);
    }

組裝Wrapper類

組裝Wrapper發生在取得SPI服務提供者實例時即下面代碼執行時

 Order extension = (Order)ExtensionLoader.getExtensionLoader(Order.class).getExtension(extName);
    /**
     * 根據指定名字取得實例
     */
    @SuppressWarnings("unchecked")
    public T getExtension(String name) {
        if (StringUtils.isEmpty(name)) {
            throw new IllegalArgumentException("Extension name == null");
        }
        if ("true".equals(name)) {
            return getDefaultExtension();
        }
        final Holder<Object> holder = getOrCreateHolder(name);
        // 通過緩存獲取
        Object instance = holder.get();
        // 雙重檢查鎖判定緩存中不存在時創建實例
        if (instance == null) {
            synchronized (holder) {
                instance = holder.get();
                if (instance == null) {
                    // 在這裏創建的實例
                    instance = createExtension(name);
                    // 將實例放入緩存中
                    holder.set(instance);
                }
            }
        }
        return (T) instance;
    }
    @SuppressWarnings("unchecked")
    private T createExtension(String name) {
        Class<?> clazz = getExtensionClasses().get(name);
        if (clazz == null) {
            throw findException(name);
        }
        try {
            // 創建實例
            T instance = (T) EXTENSION_INSTANCES.get(clazz);
            if (instance == null) {
                EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
                instance = (T) EXTENSION_INSTANCES.get(clazz);
            }
            // 判斷有無注入點
            // 這裏和Spring IOC比較相似
            // 判斷該類裏面是否有set方法且該方法不是設置的基礎參數類型
            // 並且該類得是Adaptive類
            injectExtension(instance);
            // 這段代碼則是本篇的重頭戲
            // 循環Wrapper類的緩存(上面取得Class時放入的)
            Set<Class<?>> wrapperClasses = cachedWrapperClasses;
            if (CollectionUtils.isNotEmpty(wrapperClasses)) {
                for (Class<?> wrapperClass : wrapperClasses) {
                    // 通過含參的構造方法將SPI實例(根據指定名字創建好的)注入進去
                    // 注入成功並創建好實例之後會把這個組裝好的Wrapper實例返回
                    // 這樣循環到下一個Wrapper類時其實注入的是上一個Wrapper類實例
                    // 這也解釋了爲什麼後定義的先執行
                    instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                }
            }
            return instance;
        } catch (Throwable t) {
            throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
                    type + ") couldn't be instantiated: " + t.getMessage(), t);
        }
    }

我們舉個例子比如說我們在服務提供者文件中定義瞭如下兩個Wrapper類
wrapper1=xxxxxxxxxxxxxxxxxxxxxxxxxx
wrapper2=xxxxxxxxxxxxxxxxxxxxxxxxxx

class Wrapper1 implements Service {
    // 這裏進行組裝式注入的是真正的Service服務提供者實例
    private Service service;
    public Wrapper1 (Service  service) {
        this.service = service;
    }

    @Override
    public void dosomething() {
        System.out.println("before1");
        service.dosomething();
        System.out.println("after1");
    }
}
class Wrapper2 implements Service {
    // 這裏注入的是組裝完成的Wrapper1
    private Service service;
    public Wrapper2 (Service  service) {
        this.service = service;
    }

    @Override
    public void dosomething() {
        System.out.println("before2");
        service.dosomething();
        System.out.println("after2");
    }
}

根據上面注入的結果最後執行的打印的順序是
before2
before1
業務邏輯
after1
after2
通過這樣的組裝,看好可以實現一層包裹一層,從而實現各個Wrapper以及服務提供者類之間進行解耦

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