xkernel微內(nèi)核系統(tǒng)核心包

xkernel微內(nèi)核系統(tǒng)工具

簡介

xkernel是一個基于java SPI思想的類加載工具包,是構(gòu)建微內(nèi)核系統(tǒng)的基礎(chǔ),微內(nèi)核不與擴展點的具體實現(xiàn)產(chǎn)生交互,通過ExtensionLoader將擴展點與具體實現(xiàn)建立關(guān)聯(lián),微內(nèi)核只需要知道自己暴露的擴展點和ExtensionLoader即可,擴展千變化萬,內(nèi)核以不變應(yīng)萬變。采用本工具包可快速設(shè)計一個基于微內(nèi)核+插件式的擴展開發(fā)框架,不需要改動源碼就可以實現(xiàn)擴展,解耦,實現(xiàn)擴展對原來的代碼幾乎沒有侵入性,只需要添加配置就可以實現(xiàn)擴展,符合開閉原則。

背景

SPI全稱Service Provider Interface,是Java提供的一套用來被第三方實現(xiàn)或者擴展的接口,它可以用來啟用框架擴展和替換組件,SPI的作用就是為這些被擴展的API尋找服務(wù)實現(xiàn),從java spi的原理中可以了解到,java的spi機制有著如下的弊端:

  1. 只能遍歷所有的實現(xiàn),并全部實例化。
  2. 配置文件中只是簡單的列出了所有的擴展實現(xiàn),而沒有給他們命名。導(dǎo)致在程序中很難去準(zhǔn)確的引用它們。

軟件架構(gòu)

軟件架構(gòu)說明


類結(jié)構(gòu)圖
微內(nèi)核系統(tǒng)設(shè)計簡圖
ExtensionLoader類加載過程流程圖

微內(nèi)核不與擴展點的具體實現(xiàn)產(chǎn)生交互,通過ExtensionLoader將擴展點與具體實現(xiàn)建立關(guān)聯(lián),微內(nèi)核只需要知道自己暴露的擴展點和ExtensionLoader即可,擴展千變化萬,內(nèi)核以不變應(yīng)萬變。

l 術(shù)語說明:

1, SPI:Service Provider Interface 。

2, 擴展點:被@Spi注解的 Interface 為一個擴展點。

3, 擴展:被@Spi注解的Interface 的實現(xiàn)稱為這個擴展點的一個擴展。

l 擴展點約定:

1, 擴展點必須是Interface類型,必須被@Spi注解,滿足這兩點才是一個擴展點。

l 擴展定義約定:

1, 在META-INF/services/$擴展點接口的全類名

META-INF/ext/$擴展點接口的全類名 ,

META-INF/ext/internal/$擴展點接口的全類名 ,

這些路徑下定義的文件名稱為 $擴展點接口的全類名 , 文件中以鍵值對的方式配置擴展點的擴展實現(xiàn)。例如文件 META-INF/ext/internal/com.ximg.api.ImgHandler中定義的擴展 :

thumbnailator=com.ximg.impl.ThumbnailatorImgHandler

l 默認擴展:

1, 被@Spi("abc")注解的Interface,那么這個擴展點的缺省適應(yīng)擴展就是 SPI 配置文件中 key 為 "abc" 的擴展。

l Spi注解類,該注解作用于擴展點的接口上,表明該接口是一個擴展點,屬性 value 用來指定默認適配擴展點的名稱。定義如下:

@Documented

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.TYPE)

public @interface Spi {

? String value() default "";

}

l ExtensionLoader:擴展點實現(xiàn)加載器。

1, 擴展加載器是本方案的核心組件,它控制內(nèi)部所有擴展點的初始化、加載擴展的過程。

2, 包含的靜態(tài)屬性:

EXTENSION_LOADERS:保存了內(nèi)核開放的擴展點對應(yīng)的 ExtensionLoader 實例對象。

EXTENSION_INSTANCES:保存了擴展類型 (Class) 和擴展類型的實例對象。

type : 被 @SPI 注解的 Interface , 也就是擴展點。

cachedNames : 保存不滿足裝飾模式(不存在只有一個參數(shù),并且參數(shù)是擴展點類型實例對象的構(gòu)造函數(shù))的擴展的名稱。

cachedClasses : 保存不滿足裝飾模式的擴展的 Class 實例 , 擴展的名稱作為 key , Class 實例作為 value。

cachedInstances : 保存擴展的名稱和實例對象 , 擴展名稱為 key , 擴展實例為 value。

cachedDefaultName : 擴展點上 @SPI 注解指定的缺省適配擴展。

cachedWrapperClasses : 滿足裝飾模式的擴展的 Class 實例。

exceptions : 保存在加載擴展點配置文件時,加載擴展點過程中拋出的異常 , key 是當(dāng)前讀取的擴展點配置文件的一行 , value 是拋出的異常。

3, 類加載過程:

首先通過 ExtensionLoader 的 getExtensionLoader 方法獲取一個 ExtensionLoader 實例,然后再通過 ExtensionLoader 的 getExtension 方法獲取拓展類對象。這其中,getExtensionLoader 方法用于從緩存中獲取與拓展類對應(yīng)的 ExtensionLoader,若緩存未命中,則創(chuàng)建一個新的實例,主要有以下步驟:

a. T getExtension(String name)方法:首先檢查緩存,緩存未命中則創(chuàng)建拓展對象

b. T createExtension(String name) 方法,包含了如下的步驟:

i. 通過 getExtensionClasses 獲取所有的拓展類

ii. 通過反射創(chuàng)建拓展對象

c. Map<String, Class<?>> getExtensionClasses():在通過名稱獲取拓展類之前,首先需要根據(jù)配置文件解析出拓展項名稱到拓展類的映射關(guān)系表(Map<名稱, 拓展類>),之后再根據(jù)拓展項名稱從映射關(guān)系表中取出相應(yīng)的拓展類即可,這里也是先檢查緩存,若緩存未命中,則通過 synchronized 加鎖。加鎖后再次檢查緩存,并判空。此時如果 classes 仍為 null,則通過 loadExtensionClasses 加載拓展類。

d. Map<String, Class<?>> loadExtensionClasses():loadExtensionClasses 方法總共做了兩件事情,一是對 SPI 注解進行解析,二是調(diào)用 loadDirectory 方法加載指定文件夾配置文件。

e. Void loadDirectory(Map<String, Class<?>> extensionClasses, String dir): loadDirectory 方法先通過 classLoader 獲取所有資源鏈接,然后再通過 loadResource 方法加載資源。

f. Void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL): loadResource 方法用于讀取和解析配置文件,并通過反射加載類,最后調(diào)用 loadClass 方法進行其他操作。

g. void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException:loadClass 方法用于主要用于操作緩存,如 cachedAdaptiveClass、cachedWrapperClasses 和 cachedNames 等等。

采用本工具包可快速設(shè)計一個基于微內(nèi)核+插件式的擴展開發(fā)框架,不需要改動源碼就可以實現(xiàn)擴展,解耦,實現(xiàn)擴展對原來的代碼幾乎沒有侵入性,只需要添加配置就可以實現(xiàn)擴展,符合開閉原則。

安裝教程

引入微內(nèi)核系統(tǒng)核心包

      <dependency>
    <groupId>com.javacoo</groupId>
    <artifactId>xKernel</artifactId>
    <version>1.0.0</version>
      </dependency>

使用說明

1,設(shè)計需要擴展的接口類,如:。

/**
 * 數(shù)據(jù)處理
 * <p>說明:</p>
 * <li></li>
 *
 * @Author DuanYong
 * @Since 2019/8/30 23:41
 * @Version 1.0
 */
public interface DataHandler<T> {
    /**
     * 處理
     * <p>說明:</p>
     * <li></li>
     * @Author DuanYong
     * @Since 2019/8/30 23:42
     * @Version 1.0
     * @Params data
     */
    void handle(final T data);
}

2,在接口上添加注解@Spi,如:

/**
 * 數(shù)據(jù)處理
 * <p>說明:</p>
 * <li></li>
 *
 * @Author DuanYong
 * @Since 2019/8/30 23:41
 * @Version 1.0
 */
@Spi("default")
public interface DataHandler<T> {
    /**
     * 處理
     * <p>說明:</p>
     * <li></li>
     * @Author DuanYong
     * @Since 2019/8/30 23:42
     * @Version 1.0
     * @Params data
     */
    void handle(final T data);
}

3,實現(xiàn)擴展接口類。

/**
 * 抽象數(shù)據(jù)處理類
 * <p>說明:</p>
 * <li></li>
 *
 * @Author DuanYong
 * @Since 2019/8/30 23:47
 * @Version 1.0
 */
@Slf4j
public abstract class AbstractDataHandler<T> implements DataHandler<String> {
    protected ExecutorService executorService = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(), Runtime.getRuntime().availableProcessors(),
            0L, TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<Runnable>(),Executors.defaultThreadFactory());
    @Override
    public final void handle(final String data){
        executorService.submit(()->{
            //解析
            T t = parser(data);
            if(null == t){
                return;
            }
            //執(zhí)行處理
            doHandle(t);
        });
    }
    /**
     * 執(zhí)行處理
     * <p>說明:</p>
     * <li></li>
     * @Author DuanYong
     * @Since 2019/8/30 23:57
     * @Version 1.0
     * @Params t
     */
    protected abstract void doHandle(T t);

    /**
     * 解析數(shù)據(jù)
     * <p>說明:</p>
     * <li></li>
     * @Author DuanYong
     * @Since 2019/8/30 23:55
     * @Version 1.0
     * @Params rawData 原始數(shù)據(jù)
     * @Return T
     */
    protected abstract T parser(String rawData);


}

4,在項目或jar包的META-INF/services/或者META-INF/ext或者META-INF/ext/internal目錄下,創(chuàng)建一個文本文件:名稱為接口的“全限定名”,內(nèi)容格式為:實現(xiàn)名=實現(xiàn)類的全限定名。

文件:com.javacoo.swing.api.data.DataHandler
內(nèi)容:default=com.javacoo.swing.core.data.AliPayDataHandler

5,使用ExtensionLoader.getExtensionLoader(接口類型)方法,獲取對應(yīng)接口類型的ExtensionLoader<接口類型> 實例對象。

 /**數(shù)據(jù)處理服務(wù)*/
    private DataHandler dataHandler;
    public MyNetworkDelegate(){
        dataHandler = ExtensionLoader.getExtensionLoader(DataHandler.class).getDefaultExtension();
    }

6,使用ExtensionLoader<接口類型> 實例對象,調(diào)用getExtension(擴展點名稱)方法,獲取對應(yīng)擴展點名稱的擴展實現(xiàn)。

項目信息
路漫漫其修遠兮,吾將上下而求索
碼云:https://gitee.com/javacoo
QQ:164863067
作者/微信:javacoo
郵箱:xihuady@126.com

源碼下載地址

https://gitee.com/javacoo/xkernel

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容