五、Dubbo的SPI機制分析4-Dubbo經過Wrapper實現AOP

一、Dubbo的經過Wrapper實現AOP例子

閱讀此篇文章以前,建議先學習該系列前面幾篇文章,會有一個更好的理解.
參考: Dubbo的SPI機制分析2-Adaptive詳解Dubbo的SPI機制分析3-Dubbo的IOC依賴注入java

// 在原有接口、實現類的基礎上再加一個Wrapper實現類
public class AdaptiveExtWrapper implements AdaptiveExt {

    private AdaptiveExt adaptiveExt;

    // 實現一個Wrapper類的關鍵就是提供一個帶相似參數的構造函數,後面代碼會分析
    public AdaptiveExtWrapper(AdaptiveExt adaptiveExt) {
        this.adaptiveExt = adaptiveExt;
    }

    @Override
    public String echo(String msg, URL url) {
        // do something,實現了AOP
        System.out.println("before");

        adaptiveExt.echo(msg, url);

        System.out.println("after");
        // do something
        return "wrapper";
    }
}
public class DubboAdaptiveExt implements AdaptiveExt {

    private AdaptiveExt adaptiveExt;

    // 此處DubboAdaptiveExt依賴注入一個AdaptiveExt 類型的實例,此處測試用例注入的是ThriftAdaptiveExt
    public void setAdaptiveExt(AdaptiveExt adaptiveExt) {
        this.adaptiveExt = adaptiveExt;
    }

    @Override
    public String echo(String msg, URL url) {
        System.out.println(this.adaptiveExt.echo(msg, url));
        return "dubbo";
    }
}

@Test
public void test1(){
    ExtensionLoader<AdaptiveExt> loader = ExtensionLoader.getExtensionLoader(AdaptiveExt.class);
    AdaptiveExt adaptiveExtension = loader.getExtension("dubbo");
    URL url = URL.valueOf("test://localhost/test");
    adaptiveExtension.echo("d", url);
}

同時記得dubbo的配置文件中要新增一行,不然dubbo框架沒法掃描到Wrappersegmentfault

dubbo=com.alibaba.dubbo.demo.provider.adaptive.impl.DubboAdaptiveExt
cloud=com.alibaba.dubbo.demo.provider.adaptive.impl.SpringCloudAdaptiveExt
thrift=com.alibaba.dubbo.demo.provider.adaptive.impl.ThriftAdaptiveExt
com.alibaba.dubbo.demo.provider.adaptive.impl.AdaptiveExtWrapper
運行代碼,輸出:
before
thrift
after

二、Dubbo的經過Wrapper實現AOP源碼分析

能夠發現,上述代碼前後輸出before、after,中間輸出thrift,這其實能夠說明adaptiveExtension = loader.getExtension("dubbo")返回的是AdaptiveExtWrapper類型的實例,有點像靜態代理,下面分析源代碼.app

// 核心代碼,刪去一些不重要代碼
private T createExtension(String name) {
    // 這裏也是經過dubbo的SPI機制去掃描dubbo的默認目錄下的文件,去加載實現了AdaptiveExt的實現類,這裏會
    // 加載4個,包括AdaptiveExtWrapper,它就是下面的wrapperClasses中僅有的元素,下面先分析這個加載過程
    Class<?> clazz = getExtensionClasses().get(name);
   
    try {
        T instance = (T) EXTENSION_INSTANCES.get(clazz);
        if (instance == null) {
            EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
            instance = (T) EXTENSION_INSTANCES.get(clazz);
        }
        // 依賴注入,這裏注入的就是ThriftAdaptiveExt類型的實例對象
        injectExtension(instance);
        
        // 因此這裏面cachedWrapperClasses包含了AdaptiveExtWrapper類
        Set<Class<?>> wrapperClasses = cachedWrapperClasses;
        if (wrapperClasses != null && !wrapperClasses.isEmpty()) {
            for (Class<?> wrapperClass : wrapperClasses) {
                // 先分析僅有一個Wrapper類的狀況,將原有的instance做爲參數傳入AdaptiveExtWrapper
                // 生成一個AdaptiveExtWrapper類型的instance,併爲該instance依賴注入,這裏的instance
                // 沒有什麼好注入的
                instance = injectExtension((T) wrapperClass.getConstructor(type).
                                                                          newInstance(instance));
            }
        }
        return instance;
    } 
}
private void loadClass(Map<String, Class<?>> extensionClasses, 
              java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
    // 刪去無關代碼
    // 判斷是不是Wrapper類型
    else if (isWrapperClass(clazz)) {
        Set<Class<?>> wrappers = cachedWrapperClasses;
        if (wrappers == null) {
            cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
            wrappers = cachedWrapperClasses;
        }
        // 是的話,會用HashSet記錄下它,這說明wrapper類能夠配置多個,後面分析多個的狀況
        wrappers.add(clazz);
    }
}
// 這裏判斷是Wrapper類的邏輯很簡單,若是該clazz裏面有type類型的構造參數,那麼它就是,不然會拋出異常,返回false
 private boolean isWrapperClass(Class<?> clazz) {
    try {
        clazz.getConstructor(type);
        return true;
    } catch (NoSuchMethodException e) {
        return false;
    }
}

觀看下圖能夠發現,最後生成的instance是AdaptiveExtWrapper類型的,它裏面有一個DubboAdaptiveExt類型變量,adaptiveExt裏面有一個ThriftAdaptiveExt類型的變量,因此最後調用adaptiveExtension.echo("d", url)時的adaptiveExtension是AdaptiveExtWrapper類型的,因此輸出結果如上,有了AOP的效果.
clipboard.png
剛剛分析的只有一個Wrapper類的狀況,如今分析有兩個的狀況,它的Debug結果以下圖,清晰易懂
clipboard.png框架

輸出結果是:
before
before2
thrift
after2
after
更新後的配置文件:
dubbo=com.alibaba.dubbo.demo.provider.adaptive.impl.DubboAdaptiveExt
cloud=com.alibaba.dubbo.demo.provider.adaptive.impl.SpringCloudAdaptiveExt
thrift=com.alibaba.dubbo.demo.provider.adaptive.impl.ThriftAdaptiveExt
com.alibaba.dubbo.demo.provider.adaptive.impl.AdaptiveExtWrapper
com.alibaba.dubbo.demo.provider.adaptive.impl.AdaptiveExtWrapper2
public class AdaptiveExtWrapper2 implements AdaptiveExt {

    private AdaptiveExt adaptiveExt;

    public AdaptiveExtWrapper2(AdaptiveExt adaptiveExt) {
        this.adaptiveExt = adaptiveExt;
    }

    @Override
    public String echo(String msg, URL url) {
        // do something,實現了AOP
        System.out.println("before2");

        adaptiveExt.echo(msg, url);

        System.out.println("after2");
        // do something
        return "wrapper";
    }
}
相關文章
相關標籤/搜索