追蹤解析 Dubbo 的 Spi 機制源碼

零 前期準備
0 FBI WARNING

文章異常囉嗦且繞彎。

1 版本

JDK 版本 : Adoptopenjdk 14.0.1

IDE : idea 2020.2

Dubbo 版本 : dubbo 2.7.6

2 Spi 簡介

Dubbo Spi 是 Dubbo 框架擴展性的根本基礎,是基於 jdk spi 的封裝和擴展。

3 Demo

3.1 需要擴展的接口類

import org.apache.dubbo.common.extension.Adaptive;
import org.apache.dubbo.common.extension.SPI;

@SPI("dubbo")  // spi 最主要的註解
public interface SpiDemoService {
    void sayHello(); 
}

3.2 接口實現類

一號實現類 :

public class SpiDemoOneServiceImpl implements SpiDemoService {
    @Override
    public void sayHello() {
        System.out.println("hello 1");
    }
}

二號實現類 :

public class SpiDemoTwoServiceImpl implements SpiDemoService {
    @Override
    public void sayHello() {
        System.out.println("hello 2");
    }
}

測試方法類:

import org.apache.dubbo.common.extension.ExtensionLoader;

public class SpiTest {

    public static void main(String[] args) {

        // 獲取 loader 工廠類
        ExtensionLoader<SpiDemoService> loader 
            = ExtensionLoader
                .getExtensionLoader(SpiDemoService.class);

        // 獲取實體類
        SpiDemoService one = loader.getExtension("one");

        // 測試方法
        one.sayHello();
    }
}

一 getExtensionLoader

回到 demo :

ExtensionLoader<SpiDemoService> loader 
            = ExtensionLoader
                .getExtensionLoader(SpiDemoService.class);

1 getExtensionLoader

getExtensionLoader 是 ExtensionLoader 最核心的靜態方法,用於獲取 ExtensionLoader 實例

// org.apache.dubbo.common.extension.ExtensionLoader
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {

        // 可行性判斷,均忽略
        
        if (type == null) {
            throw new IllegalArgumentException("Extension type == null");
        }
        if (!type.isInterface()) {
            throw new IllegalArgumentException("Extension type (" + type + ") is not an interface!");
        }
        if (!withExtensionAnnotation(type)) {
            throw new IllegalArgumentException("Extension type (" + type +
                    ") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!");
        }


        // 先嚐試獲取,如果獲取失敗就創建一個 ExtensionLoader
        // EXTENSION_LOADERS 是一個靜態 ConcurrentHashMap,用來存放 loader
        ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
        if (loader == null) {
            EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
            loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
        }
        return loader;
    }

2 ExtensionLoader 構造器

// org.apache.dubbo.common.extension.ExtensionLoader
private ExtensionLoader(Class<?> type) {
    // 存儲要創建的 loader 的類型
    this.type = type;
    
    // objectFactory 是 ExtensionFactory 類型的對象,是用來依賴注入的工廠
    objectFactory 
        = (type == ExtensionFactory.class ? 
          null : ExtensionLoader
                     .getExtensionLoader(ExtensionFactory.class)
                     .getAdaptiveExtension());
    }

二 getAdaptiveExtension

1 getAdaptiveExtension

// org.apache.dubbo.common.extension.ExtensionLoader
public T getAdaptiveExtension() {
    // cachedAdaptiveInstance 是一個 Holder 對象,Holder 是對 Object 的包裝
    // 在此處先嚐試獲取實例化完成的對象,如果獲取不到,就進入加載邏輯
    Object instance = cachedAdaptiveInstance.get();
    if (instance == null) {
    
        // 如果之前初始化的時候報錯了,那麼錯誤會被記錄下來並緩存在此處,直接拋出
        // 此處的設計應該是減少鎖消耗
        if (createAdaptiveInstanceError != null) {
            throw new IllegalStateException("Failed to create adaptive instance: " + createAdaptiveInstanceError.toString(),
            createAdaptiveInstanceError);
        }

        synchronized (cachedAdaptiveInstance) {
            // 雙鎖驗證
            instance = cachedAdaptiveInstance.get();
            if (instance == null) {
                try {
                    // 創建實例並存儲在 Holder 對象裏
                    instance = createAdaptiveExtension();
                    cachedAdaptiveInstance.set(instance);
                } catch (Throwable t) {
                    // 如果報錯了就會把錯誤存儲起來
                    createAdaptiveInstanceError = t;
                    throw new IllegalStateException("Failed to create adaptive instance: " + t.toString(), t);
                }
            }
        }
    }
    
    // 返回對象實例
    return (T) instance;
}

2 createAdaptiveExtension

創建實例對象:

// org.apache.dubbo.common.extension.ExtensionLoader
private T createAdaptiveExtension() {
    try {
        // getAdaptiveExtensionClass().newInstance() 方法會使用 Class 對象的 newInstance() 方法創建一個對象
        // injectExtension(...) 則會對創建出來的對象進行依賴注入
        return injectExtension((T)getAdaptiveExtensionClass().newInstance());
    } catch (Exception e) {
        throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e);
    }
}

3 injectExtension

// org.apache.dubbo.common.extension.ExtensionLoader
private T injectExtension(T instance) {

    // objectFactory 是用來依賴注入的 ExtensionFactory
    // 如果 objectFactory 爲空,就直接返回
    // 需要注意的是,只有非 ExtensionFactory 的 loader 纔有 objectFactory
    if (objectFactory == null) {
        return instance;
    }

    try {
        // 輪訓實例對象中所有的方法
        for (Method method : instance.getClass().getMethods()) {
        
            // 如果方法不是 set 方法就跳過
            // 此處可以理解爲,dubbo 的 spi 依賴注入需要 set 方法支持
            if (!isSetter(method)) {
                continue;
            }

            // 如果方法被標註了 DisableInject 註解就跳過
            if (method.getAnnotation(DisableInject.class) != null) {
                continue;
            }
            
            // 如果方法的參數是原始類型就跳過
            // 依賴注入需要使用包裝類型
            Class<?> pt = method.getParameterTypes()[0];
            if (ReflectUtils.isPrimitives(pt)) {
                continue;
            }

            try {
            
                // 反射注入
                String property = getSetterProperty(method);
                Object object = objectFactory.getExtension(pt, property);
                if (object != null) {
                    method.invoke(instance, object);
                }
            } catch (Exception e) {
                logger.error("Failed to inject via method " + method.getName()
                             + " of interface " + type.getName() + ": " + e.getMessage(), e);
            }

        }
    } catch (Exception e) {
        logger.error(e.getMessage(), e);
    }
    return instance;
}

三 ExtensionFactory

ExtensionFactory 是用來依賴注入的工廠:

@SPI
public interface ExtensionFactory {
    <T> T getExtension(Class<T> type, String name);
}

該接口在 Dubbo 中有三個默認實現類:

org.apache.dubbo.config.spring.extension.SpringExtensionFactory
org.apache.dubbo.common.extension.factory.AdaptiveExtensionFactory
org.apache.dubbo.common.extension.factory.SpiExtensionFactory
在開發中 SpringExtensionFactory 應該會用的更廣泛一些,本示例中此處暫時不展開。

1 AdaptiveExtensionFactory

AdaptiveExtensionFactory 是默認工廠:

import org.apache.dubbo.common.extension.Adaptive;
import org.apache.dubbo.common.extension.ExtensionFactory;
import org.apache.dubbo.common.extension.ExtensionLoader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory {

    private final List<ExtensionFactory> factories;

    // 構造器
    public AdaptiveExtensionFactory() {
        ExtensionLoader<ExtensionFactory> loader 
            = ExtensionLoader
                .getExtensionLoader(ExtensionFactory.class);
        
        // AdaptiveExtensionFactory 會將 SpiExtensionFactory 和 SpringExtensionFactory 放置在 factories 列表裏
        
        List<ExtensionFactory> list = new ArrayList<ExtensionFactory>();
        for (String name : loader.getSupportedExtensions()) {
            list.add(loader.getExtension(name));
        }
        factories = Collections.unmodifiableList(list);
    }

    // 使用 AdaptiveExtensionFactory 去獲取實體類的時候,
    // 會調用 spi 或者 spring 的 ext 工廠去嘗試獲取實體類
    @Override
    public <T> T getExtension(Class<T> type, String name) {
        for (ExtensionFactory factory : factories) {
            T extension = factory.getExtension(type, name);
            if (extension != null) {
                return extension;
            }
        }
        return null;
    }
}

2 SpiExtensionFactory

import org.apache.dubbo.common.extension.ExtensionFactory;
import org.apache.dubbo.common.extension.ExtensionLoader;
import org.apache.dubbo.common.extension.SPI;

public class SpiExtensionFactory implements ExtensionFactory {

    @Override
    public <T> T getExtension(Class<T> type, String name) {
        // spi 工廠用於解析 spi 註解
        if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
            // 如果傳入的 type 是一個被 spi 註釋的接口,那麼會初始化一個它的 class loader
            ExtensionLoader<T> loader 
                    = ExtensionLoader.getExtensionLoader(type);
                    
            // 初始化對象
            if (!loader.getSupportedExtensions().isEmpty()) {
                return loader.getAdaptiveExtension();
            }
        }
        return null;
    }
}

四 getExtension

回到 demo :

SpiDemoService one = loader.getExtension("one");

1 getExtension

// org.apache.dubbo.common.extension.ExtensionLoader
public T getExtension(String name) {

    // 非空驗證,忽略
    if (StringUtils.isEmpty(name)) {
        throw new IllegalArgumentException("Extension name == null");
    }
    
    // 默認機制,會去找名稱爲 dubbo 的實例
    // 一般用不到
    if ("true".equals(name)) {
        return getDefaultExtension();
    }
    
    // 如果之前不存在這個名稱對應的 Holder 對象,此處會創建一個空白的 Holder
    // 調用 get() 方法會獲得空對象
    final Holder<Object> holder = getOrCreateHolder(name);
    Object instance = holder.get();
    if (instance == null) {
        synchronized (holder) {
            instance = holder.get();
            if (instance == null) {
                // 創建對象實例,並裝進 holder 中
                instance = createExtension(name);
                holder.set(instance);
            }
        }
    }
    // 返回實例
    return (T) instance;
}

2 createExtension

// org.apache.dubbo.common.extension.ExtensionLoader
private T createExtension(String name) {

    // 如果 class 不存在就報錯
    Class<?> clazz = getExtensionClasses().get(name);
    if (clazz == null) {
        throw findException(name);
    }
    
    try {
        // 嘗試獲取 class 對應的實例,如果不存在就創建
        T instance = (T) EXTENSION_INSTANCES.get(clazz);
        if (instance == null) {
            EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
            instance = (T) EXTENSION_INSTANCES.get(clazz);
        }
        
        // 進行依賴注入
        injectExtension(instance);
        Set<Class<?>> wrapperClasses = cachedWrapperClasses;
        if (CollectionUtils.isNotEmpty(wrapperClasses)) {
            for (Class<?> wrapperClass : wrapperClasses) {
                instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
            }
        }
        initExtension(instance);
        return instance;
    } catch (Throwable t) {
        throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
                                        type + ") couldn't be instantiated: " + t.getMessage(), t);
    }
}

五 一點嘮叨

1 總結

spi 的代碼有點繞,做一下總結。

ExtensionLoader

ExtensionLoader 是整個 spi 系統的門面,也是 spi 的實例結合類。

內置對象

EXTENSION_LOADERS
記錄了 class 和 loader 的對應關係

EXTENSION_INSTANCES
記錄了 class 和實例對象的對應關係。可以認爲是一個靜態的全局 bean 容器。

主要方法

getExtensionLoader(Class)
嘗試創建這個 class 對應的 loader 對象;

但是在創建一般 spi 接口的 loader 對象之前,還會先創建 ExtensionFactory 的 loader 對象。

getExtension(String)
按照需求實例化對象,放置到 EXTENSION_INSTANCES 對象中,然後做依賴注入並返回給使用者。

ExtensionFactory

用來做依賴注入的工廠。

ExtensionFactory 也是被 ExtensionLoader 管理的一類特殊的 spi 類。

2 未完成

dubbo 的 spi 還有很重要的一部分即爲 @Adaptive 註解的使用,這部分涉及動態代理,較爲複雜,有空開新篇講。

本文僅爲個人的學習筆記,可能存在錯誤或者表述不清的地方,有緣補充

來源:https://www.tuicool.com/articles/fMBzMvU

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