Dubbo源碼解析-SPI.Dubbo可擴展機制原理

1.背景,改進


     SPI的全名爲Service Provider Interface,面向對象的設計裏面,模塊之間推薦基於接口編程,而不是對實現類進行硬編碼,這樣做也是爲了模塊設計的可拔插原則。爲了在模塊裝配的時候不在程序裏指明是哪個實現,就需要一種服務發現的機制,jdk的spi就是爲某個接口尋找服務實現

   瞭解Dubbo/閱讀過官網文檔的同學,可以看到Dubbo內部存在着大量的擴展,如Protocol:DubboProtocol,InJvmProtocol; Cluster:FailoverCluster,FailFastCluser;LoadBalance:RandomLoadBalance,LeastActiveLoadBalance等等;
    Dubbo的SPI擴展機制主要在JDK的擴展機制上進行了改進:主要原因如下

  • 需要遍歷所有的實現,並實例化,然後我們在循環中才能找到我們需要的實現。
  • 配置文件中只是簡單的列出了所有的擴展實現,而沒有給他們命名。導致在程序中很難去準確的引用它們。
  • 擴展如果依賴其他的擴展,做不到自動注入和裝配
  • 不提供類似於Spring的IOC和AOP功能
  • 擴展很難和其他的框架集成,比如擴展裏面依賴了一個Spring bean,原生的Java SPI不支持

2.Dubbo—SPI常見術語(摘自官網)

    2.1 擴展點(Extension Point)

           是一個Java的接口。

    2.2 擴展(Extension)

           擴展點的實現類。

    2.3 擴展實例(Extension Instance)

           擴展點實現類的實例。

    2.4 擴展自適應實例(Extension Adaptive Instance)

     第一次接觸這個概念時,可能不太好理解(我第一次也是這樣的...)。如果稱它爲擴展代理類,可能更好理解些。擴展的自適應 實例其實就是一個Extension的代理,它實現了擴展點接口。在調用擴展點的接口方法時,會根據實際的參數來決定要使用哪個擴 展。比如一個IRepository的擴展點,有一個save方法。有兩個實現MysqlRepository和MongoRepository。IRepository的自適應實例在調用接口方法的時候,會根據save方法中的參數,來決定要調用哪個IRepository的實現。如果方法參數中有repository=mysql,那麼就調用MysqlRepository的save方法。如果repository=mongo,就調用MongoRepository的save方法。和麪向對象的延遲綁定很類似。爲什麼Dubbo會引入擴展自適應實例的概念呢?

  • Dubbo中的配置有兩種,一種是固定的系統級別的配置,在Dubbo啓動之後就不會再改了。還有一種是運行時的配置,可能對於每一次的RPC,這些配置都不同。比如在xml文件中配置了超時時間是10秒鐘,這個配置在Dubbo啓動之後,就不會改變了。但針對某一次的RPC調用,可以設置它的超時時間是30秒鐘,以覆蓋系統級別的配置。對於Dubbo而言,每一次的RPC調用的參數都是未知的。只有在運行時,根據這些參數才能做出正確的決定。
  • 很多時候,我們的類都是一個單例的,比如Spring的bean,在Spring bean都實例化時,如果它依賴某個擴展點,但是在bean實例化時,是不知道究竟該使用哪個具體的擴展實現的。這時候就需要一個代理模式了,它實現了擴展點接口,方法內部可以根據運行時參數,動態的選擇合適的擴展實現。而這個代理就是自適應實例。 自適應擴展實例在Dubbo中的使用非常廣泛,Dubbo中,每一個擴展都會有一個自適應類,如果我們沒有提供,Dubbo會使用字節碼工具爲我們自動生成一個。所以我們基本感覺不到自適應類的存在。後面會有例子說明自適應類是怎麼工作的。

    2.5 @SPI

      @SPI註解作用於擴展點的接口上,表明該接口是一個擴展點。可以被Dubbo的ExtentionLoader加載。如果沒有此ExtensionLoader調用會異常。

   2.6 @Adaptive

    @Adaptive註解用在擴展接口的方法上。表示該方法是一個自適應方法。Dubbo在爲擴展點生成自適應實例時,如果方法有@Adaptive註解,會爲該方法生成對應的代碼。方法內部會根據方法的參數,來決定使用哪個擴展。 @Adaptive註解用在類上代表實現一個裝飾類,類似於設計模式中的裝飾模式,它主要作用是返回指定類,目前在整個系統中AdaptiveCompiler、AdaptiveExtensionFactory這兩個類擁有該註解。

   2.7 ExtentionLoader

類似於Java SPI的ServiceLoader,負責擴展的加載和生命週期維護。

    2.8 擴展別名

和Java SPI不同,Dubbo中的擴展都有一個別名,用於在應用中引用它們。比如

random=com.alibaba.dubbo.rpc.cluster.loadbalance.RandomLoadBalance
roundrobin=com.alibaba.dubbo.rpc.cluster.loadbalance.RoundRobinLoadBalance

其中的random,roundrobin就是對應擴展的別名。這樣我們在配置文件中使用random或roundrobin就可以了。

2.9 一些路徑

和Java SPI從/META-INF/services目錄加載擴展配置類似,Dubbo也會從以下路徑去加載擴展配置文件:

  • META-INF/dubbo/internal
  • META-INF/dubbo
  • META-INF/services

3.Dubbo-SPI機制測試

    項測項目結構,定義Roboot接口,定義void  sayHello()方法,注意Roboot接口定義SPI註解,SPI(key)表示默認爲value(對應鍵值對中的key);

兩個Roboot實現類,Bumbleee,OptimusPrime;
Bumbleee類:

package com.didispace.spi.impl;

import com.didispace.spi.Robot;

/**
 * @Author: SoftWareKang
 * @Name:SpringCloud-Learning
 * @Date: 2020/5/24 17:33
 */
public class Bumblebee implements Robot {

    public static final String NAME = "bumblebee";

    @Override
    public void sayHello() {
        System.out.println("I'm Bumblebee");
    }
}

OptimusPrime類:

package com.didispace.spi.impl;

import com.didispace.spi.Robot;

/**
 * @Author: SoftWareKang
 * @Name:SpringCloud-Learning
 * @Date: 2020/5/24 17:33
 */
public class OptimusPrime implements Robot {
    public static final String NAME = "optimusPrime";

    @Override
    public void sayHello() {
        System.out.println("I'm OptimusPrime");
    }
}

key-value文件:META-INF/dubbo/com.didispace.spi.Robot,文件內容如下

optimusPrime = com.didispace.spi.impl.OptimusPrime
bumblebee = com.didispace.spi.impl.Bumblebee

測試類:

@Test
	public void spiTest() {
		ExtensionLoader<Robot> extensionLoader =
				ExtensionLoader.getExtensionLoader(Robot.class);
		Robot optimusPrime = extensionLoader.getExtension("optimusPrime");
		optimusPrime.sayHello();
		Robot bumblebee = extensionLoader.getExtension("bumblebee");
		bumblebee.sayHello();
	}

測試結果:控制檯會打印如下結果;
I'm OptimusPrime
I'm Bumblebee
 

4.基本用法瞭解,探測源碼

ExtensionLoader 是最核心的類,負責擴展點的加載和生命週期管理。 ExtensionLoader 的方法比較多,比較常用的方法有:

public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type)
public T getExtension(String name)
public T getAdaptiveExtension()

getExtensionLoader方法 這是一個靜態工廠方法,入參是一個可擴展的接口,返回一個該接口的ExtensionLoader實體類。通過這個實體類,可以根據name獲得具體的擴展,也可以獲得一個自適應擴展。

 public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
        // type如果爲空返回參數異常;
        if (type == null) {
            throw new IllegalArgumentException("Extension type == null");
         // 如果type不是接口類型,返回參數異常
        } else if (!type.isInterface()) {
            throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");
        //  如果沒有@SPI註解,返回參數異常
        } else if (!withExtensionAnnotation(type)) {
            throw new IllegalArgumentException("Extension type(" + type + ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
        } else {
        // 獲取ExtensionLoader實例
            ExtensionLoader<T> loader = (ExtensionLoader)EXTENSION_LOADERS.get(type);
            if (loader == null) {
                // 放入緩存中
                EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader(type));
                // loader賦值
                loader = (ExtensionLoader)EXTENSION_LOADERS.get(type);
            }
            
            return loader;
        }
    }


  private ExtensionLoader(Class<?> type) {
        this.type = type;
        objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
// 根據name即key獲取對用接口實現類的實例
public T getExtension(String name) {
        // 判斷參數:如果name不爲null或不爲"",否則返回參數異常
        if (name != null && name.length() != 0) {
            if ("true".equals(name)) {
                return this.getDefaultExtension();
            } else {
                // 緩存中獲取holdler:可以理解爲對一個對象持有的工具類
                Holder<Object> holder = (Holder)this.cachedInstances.get(name);
                if (holder == null) {
                    // 沒有則創建holder
                    this.cachedInstances.putIfAbsent(name, new Holder());
                    holder = (Holder)this.cachedInstances.get(name);
                }
                // 獲取該對象實例的值
                Object instance = holder.get();
                // 標準的雙重判斷
                if (instance == null) {
                    synchronized(holder) {
                        instance = holder.get();
                        if (instance == null) {
                            // 核心方法,創建實例
                            instance = this.createExtension(name);
                            holder.set(instance);
                        }
                    }
                }

                return instance;
            }
        } else {
            throw new IllegalArgumentException("Extension name == null");
        }
    }
   // holder類
public class Holder<T> {
    private volatile T value;

    public Holder() {
    }

    public void set(T value) {
        this.value = value;
    }

    public T get() {
        return this.value;
    }
}

核心方法private T createExtension(String name)方法

 private T createExtension(String name) {
       // 根據擴展節點名稱獲取對應class
        Class<?> clazz = (Class)this.getExtensionClasses().get(name);
        if (clazz == null) {
            throw this.findException(name);
        } else {
            try {
                // 緩存中獲取實例
                T instance = EXTENSION_INSTANCES.get(clazz);
                if (instance == null) {
                    // 如果爲空,調用newInstance()方法生成實例
                    EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
                    instance = EXTENSION_INSTANCES.get(clazz);
                }
                // IOC注入屬性
                this.injectExtension(instance);
                // 如果有wrapper,添加wrapper
                Set<Class<?>> wrapperClasses = this.cachedWrapperClasses;
                Class wrapperClass;
                if (wrapperClasses != null && wrapperClasses.size() > 0) {
                    for(Iterator i$ = wrapperClasses.iterator(); i$.hasNext(); instance = this.injectExtension(wrapperClass.getConstructor(this.type).newInstance(instance))) {
                        wrapperClass = (Class)i$.next();
                    }
                }

                return instance;
            } catch (Throwable var7) {
                throw new IllegalStateException("Extension instance(name: " + name + ", class: " + this.type + ")  could not be instantiated: " + var7.getMessage(), var7);
            }
        }
    }

核心: private Map<String, Class<?>> getExtensionClasses()方法

 private Map<String, Class<?>> getExtensionClasses() {
        Map<String, Class<?>> classes = (Map)this.cachedClasses.get();
        if (classes == null) {
            Holder var2 = this.cachedClasses;
            synchronized(this.cachedClasses) {
                classes = (Map)this.cachedClasses.get();
                if (classes == null) {
                    // 根據配置,加載classes
                    classes = this.loadExtensionClasses();
                    this.cachedClasses.set(classes);
                }
            }
        }

        return classes;
    }

核心:private Map<String, Class<?>> loadExtensionClasses()方法,private void loadFile(Map<String, Class<?>> extensionClasses, String dir)方法

private Map<String, Class<?>> loadExtensionClasses() {
        // 獲取SPI默認值
        SPI defaultAnnotation = (SPI)this.type.getAnnotation(SPI.class);
        if (defaultAnnotation != null) {
            String value = defaultAnnotation.value();
            if (value != null && (value = value.trim()).length() > 0) {
                String[] names = NAME_SEPARATOR.split(value);
                // 默認值只能唯一
                if (names.length > 1) {
                    throw new IllegalStateException("more than 1 default extension name on extension " + this.type.getName() + ": " + Arrays.toString(names));
                }

                if (names.length == 1) {
                    // 緩存
                    this.cachedDefaultName = names[0];
                }
            }
        }

        Map<String, Class<?>> extensionClasses = new HashMap();
        // 加載三個目錄下的配置
        this.loadFile(extensionClasses, "META-INF/dubbo/internal/");
        this.loadFile(extensionClasses, "META-INF/dubbo/");
        this.loadFile(extensionClasses, "META-INF/services/");
        return extensionClasses;
    }
   
 
    // 加載的核心方法,extensionClasses緩存class
    private void loadFile(Map<String, Class<?>> extensionClasses, String dir) {
        // 拼接fileName
        String fileName = dir + this.type.getName();

        try {
            // 加載器
            ClassLoader classLoader = findClassLoader();
            Enumeration urls;
             // 根據文件名獲取所有URL
            if (classLoader != null) {
                urls = classLoader.getResources(fileName);
            } else {
                urls = ClassLoader.getSystemResources(fileName);
            }
            
            if (urls != null) {
                label269:
                while(urls.hasMoreElements()) {
                    java.net.URL url = (java.net.URL)urls.nextElement();

                    try {
                        BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), "utf-8"));

                        try {
                            String line = null;

                            while(true) {
                                do {
                                    if ((line = reader.readLine()) == null) {
                                        continue label269;
                                    }
                                    // 定位#字符,截取,因爲是註釋
                                    int ci = line.indexOf(35);
                                    if (ci >= 0) {
                                        line = line.substring(0, ci);
                                    }
                                    // 去除空格
                                    line = line.trim();
                                } while(line.length() <= 0);

                                try {
                                    String name = null;
                                    int i = line.indexOf(61);
                                    if (i > 0) {
                                        // 獲取key-value
                                        name = line.substring(0, i).trim();
                                        line = line.substring(i + 1).trim();
                                    }

                                    if (line.length() > 0) {
                                        // class.forname獲取class
                                        Class<?> clazz = Class.forName(line, true, classLoader);
                                        if (!this.type.isAssignableFrom(clazz)) {
                                            throw new IllegalStateException("Error when load extension class(interface: " + this.type + ", class line: " + clazz.getName() + "), class " + clazz.getName() + "is not subtype of interface.");
                                        }

                                        if (clazz.isAnnotationPresent(Adaptive.class)) {
                                            if (this.cachedAdaptiveClass == null) {
                                                this.cachedAdaptiveClass = clazz;
                                            } else if (!this.cachedAdaptiveClass.equals(clazz)) {
                                                throw new IllegalStateException("More than 1 adaptive class found: " + this.cachedAdaptiveClass.getClass().getName() + ", " + clazz.getClass().getName());
                                            }
                                        } else {
                                            try {
                                              // // 檢測 clazz 是否有默認的構造方法,如果沒有,則拋出異常
                                                clazz.getConstructor(this.type);
                                                Set<Class<?>> wrappers = this.cachedWrapperClasses;
                                                if (wrappers == null) {
                                                    this.cachedWrapperClasses = new ConcurrentHashSet();
                                                    wrappers = this.cachedWrapperClasses;
                                                }
                                                // 緩存clazz
                                                wrappers.add(clazz);
                                            } catch (NoSuchMethodException var27) {
                                                clazz.getConstructor();
                                                if (name == null || name.length() == 0) {
                                                    name = this.findAnnotationName(clazz);
                                                    if (name == null || name.length() == 0) {
                                                        if (clazz.getSimpleName().length() <= this.type.getSimpleName().length() || !clazz.getSimpleName().endsWith(this.type.getSimpleName())) {
                                                            throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + url);
                                                        }

                                                        name = clazz.getSimpleName().substring(0, clazz.getSimpleName().length() - this.type.getSimpleName().length()).toLowerCase();
                                                    }
                                                }

                                                String[] names = NAME_SEPARATOR.split(name);
                                                if (names != null && names.length > 0) {
                                                    Activate activate = (Activate)clazz.getAnnotation(Activate.class);
                                                    if (activate != null) {
                                                        this.cachedActivates.put(names[0], activate);
                                                    }

                                                    String[] arr$ = names;
                                                    int len$ = names.length;

                                                    for(int i$ = 0; i$ < len$; ++i$) {
                                                        String n = arr$[i$];
                                                        if (!this.cachedNames.containsKey(clazz)) {
                                                            this.cachedNames.put(clazz, n);
                                                        }

                                                        Class<?> c = (Class)extensionClasses.get(n);
                                                        if (c == null) {
                                                            extensionClasses.put(n, clazz);
                                                        } else if (c != clazz) {
                                                            throw new IllegalStateException("Duplicate extension " + this.type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName());
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                } catch (Throwable var28) {
                                    IllegalStateException e = new IllegalStateException("Failed to load extension class(interface: " + this.type + ", class line: " + line + ") in " + url + ", cause: " + var28.getMessage(), var28);
                                    this.exceptions.put(line, e);
                                }
                            }
                        } finally {
                            reader.close();
                        }
                    } catch (Throwable var30) {
                        logger.error("Exception when load extension class(interface: " + this.type + ", class file: " + url + ") in " + url, var30);
                    }
                }
            }
        } catch (Throwable var31) {
            logger.error("Exception when load extension class(interface: " + this.type + ", description file: " + fileName + ").", var31);
        }

    }

loadExtensionClasses()的基本流程:

1.SPI註解解析
2.調用 loadDirectory 方法加載指定文件夾配置文件。
3.loadResource 方法用於讀取和解析配置文件,並通過反射加載類;
4.緩存相關信息
 

5.Dubbo-IOC源碼
 

private T createExtension(String name)方法在創建完實例後,執行// IOC注入屬性 this.injectExtension(instance);
我們進入源碼分析

 private T injectExtension(T instance) {
        try {
            if (this.objectFactory != null) {
                // 獲取所有方法
                Method[] arr$ = instance.getClass().getMethods();
                int len$ = arr$.length;
 
                for(int i$ = 0; i$ < len$; ++i$) {
                   // 如果方法爲set方法,且級別爲public
                    Method method = arr$[i$];
                    if (method.getName().startsWith("set") && method.getParameterTypes().length == 1 && Modifier.isPublic(method.getModifiers())) {
                        Class pt = method.getParameterTypes()[0];
                        
                        try {
                            // 獲取屬性名
                            String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
                           // 通過工廠生成對象
                            Object object = this.objectFactory.getExtension(pt, property);
                           // 通過反射賦值
                            if (object != null) {
                                method.invoke(instance, object);
                            }
                        } catch (Exception var9) {
                            logger.error("fail to inject via method " + method.getName() + " of interface " + this.type.getName() + ": " + var9.getMessage(), var9);
                        }
                    }
                }
            }
        } catch (Exception var10) {
            logger.error(var10.getMessage(), var10);
        }

        return instance;
    }

在上面代碼中,objectFactory 變量的類型爲 AdaptiveExtensionFactory,AdaptiveExtensionFactory 內部維護了一個 ExtensionFactory 列表,用於存儲其他類型的 ExtensionFactory。Dubbo 目前提供了兩種 ExtensionFactory,分別是 SpiExtensionFactory 和 SpringExtensionFactory。前者用於創建自適應的拓展,後者是用於從 Spring 的 IOC 容器中獲取所需的拓展。
Dubbo-IOC目前主要是set方法,很簡單;

6.總結

  1. 生成接口類的ExtensionLoader對象:
    1. 參數判空,接口類型校驗,@SPI註解校驗
    2. 生成ExtensionLoader,objectFactory;   
  2.  根據ExtensionLoader.getExtensionName(name)獲取擴展節點實例;
    1. 查詢緩存的handler,沒有則生成handler;查詢handler的值,爲空則createExtension(name)創建對應實例;
    2. 根據配置,底層根據class.forName生成class對象實例,返回class對象
    3. 調用class.newInstance創建實例,初始化,IOC賦值等;
  3. 根據生成的實例,進行面向接口編程;

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