模塊依賴管理【續1】- 依賴循環檢測的實現

    這篇是承接《輕量級 Java 開發框架 設計》系列Blog文的後續文章,同時爲《模塊依賴管理》博文的續,本文專門用以講解循環依賴檢查在 Hasor 中是如何實現的。 java

    依賴循環引起最嚴重的問題就是死循環,想必絕大部分開發者深有體會這具體表如今 A -> B , B -> C ,C ->A。一般軟件程序中出現循環依賴都是設計不周全所致使。 git

    在 Hasor 中,經過 Module 接口定義模塊,而且經過調用接口參數的方法來聲明其第三方引用依賴。首先 Hasor 的設計目標是做爲輕量級模塊化開發,所以它在覈心部分省去了繁重了類加載機制,同時也省去了複雜的模塊配置文件。正由於如此 Hasor 在定義模塊上才能夠變得十分輕巧。 github

    一樣的 Hasor 雖然不用考慮類會被不一樣的類加載器反覆加載屢次。可是仍然會面對模塊依賴出現循環的現象。Hasor 在處理這件事採用的是以下步驟: 算法

    1.加載 Mode 並調用 Mode 的 init方法已觸發對應的生命週期階段,在期間 Mode1 能夠經過代碼的形式顯示的告訴容器它會依賴誰。
             (這一過程會在全部模塊上進行。注意:這一過程不會保證調用的順序,模塊的順序多是隨機的)
    2.若是模塊在執行 init 階段中出現了問題,那麼容器會標記這個模塊。
    3.將全部模塊放到「反應器」中進行處理。「反應器」會負責模塊的循環依賴檢查和排序。
    4.若是 A -> B,B -> C。「反應器」會打印出依賴樹,而且按照依賴狀況對模塊集合進行排序。
    5.按照最終的順序依次調用模塊的 Start 方法。
api

下面是模塊在 init 階段聲明依賴的代碼片斷:
框架

public void init(ApiBinder apiBinder) {
    DependencySettings dep = apiBinder.dependency();
    /*弱依賴,目標模塊即便沒有成功啓動也不影響當前模塊*/
    dep.weak(Mode2.class);
    /*強依賴,當前模塊的啓動必須依靠目標模塊*/
    dep.forced(Mode3.class);
}


「反應器」須要負責兩件:
    1.進行依賴循環檢查。當遇到依賴循環時拋出異常,而且定位出現問題的模塊依賴位置。
    2.根據依賴關係對依賴樹進行排序以獲得正確的啓動順序。(將在下一篇Blog文中介紹。) eclipse

循環檢測算法:
    1.準備一個空的集合,用於保存模塊包括本身在內全部依賴的模塊。
    2.將模塊自身加入到集合中。
    3.將模塊的依賴依次加入到集合,每次加入都從集合中檢查依賴是否等於自身。
    4.當處理完模塊自己依賴以後遞歸處理依賴模塊,而且重複(步驟3)的檢測。
    5.當(步驟3)判斷成當即發現循環依賴,拋出異常便可。 maven

下面這段代碼是負責循環依賴檢測的關鍵代碼:
    ModuleInfo接口:經過該接口能夠獲取模塊的依賴信息,和模塊自己元信息。
    AbstractModulePropxy類:是Module接口的代理類,該類實現了 ModuleInfo 接口,
    ReactorModuleInfoElement類:該類是負責保存記錄,檢查過程當中的信息。
    Dependency接口:是用來表示依賴的模塊。 模塊化

/**檢查模塊的依賴是否正確。*/
public void checkModule(ModuleInfo info) {
    AbstractModulePropxy infoBean = (AbstractModulePropxy) info;
    List<ReactorModuleInfoElement> stackArray = new ArrayList<ReactorModuleInfoElement>();
    try {
        //放到棧頂【ReactorModuleInfoElement(深度 0,被檢測的模塊,檢測狀態)】
        stackArray.add(new ReactorModuleInfoElement(0, infoBean, "[OK]    "));
        checkModuleInDependency(infoBean, 1, infoBean.getDependency(),
                                       new ArrayList<ModuleInfo>(), stackArray);
    } catch (RuntimeException e) {
        String treeInfo = getTreeInfo(stackArray, "[Other] ......");
        Hasor.error("%s module depend on the loop.\n%s", infoBean.getDisplayName(), treeInfo);
        throw e;
    }
}
private void checkModuleInDependency(AbstractModulePropxy info, int depth,
                                       List<Dependency> depList,
                                       List<ModuleInfo> depArray,
                                       List<ReactorModuleInfoElement> stackArray) {
    for (Dependency dep : depList) {
        AbstractModulePropxy depInfo = (AbstractModulePropxy) dep.getModuleInfo();
        ReactorModuleInfoElement infoLog = new ReactorModuleInfoElement(depth, depInfo);
        depArray.add(depInfo);
        stackArray.add(infoLog);
        //
        if (depArray.contains(info)) {
            /*這裏遇到循環依賴,應該是出現了本身依賴本身的現象。*/
            infoLog.setMark("[Error] ");
            throw new RuntimeException(depInfo.getDisplayName() +
                                       " modules depend on the loop.");
        } else {
            infoLog.setMark("[OK]    ");
            this.checkModuleInDependency(info, depth + 1, depInfo.getDependency(),
                                       depArray, stackArray);
        }
    }
}
咱們知道依賴一旦出現循環,就意味着在環中任意一點向前走最後都會走回本身。所以藉助這一特色,依賴循環檢測能夠從任意一個模塊開始。

    第 04 行:準備了一個空集合,用於保存模塊包括本身在內全部依賴的模塊。
    第 07 行:將模塊自身加入到集合中,【深度0,狀態OK
    第 08 行:調用私有方法檢查模塊的依賴。checkModuleInDependency方法各個參數的意義以下:
            1.
AbstractModulePropxy info:正在檢測的模塊
            2.int depth:當前所處深度
            3.List<Dependency> depList:要檢查的依賴模塊集合
            4.List<ModuleInfo> depArray已經檢查過的依賴集
            5.List<ReactorModuleInfoElement> stackArray:檢查過程當中產生的路徑數據,當遇到錯誤時用於打印問題模塊發生位置。
    第 11 行:當出現循環依賴時將記錄的stackArray信息,轉換成日誌。
    第 20 行:依次檢測 depList 集合中所表示的依賴模塊。
    第 22 行:建立檢測的日誌信息,後面多餘的空格是爲了打印美觀。
            若是成功檢測會被標記爲「[OK]    
」,若是失敗檢測會被標記爲「[Error] 」。
    第 23,24 行:將所依賴的項目添加到依賴集合中,而後交給第 26 行的 if 判斷是否遇到循環依賴。
             這裏的邏輯是一旦依賴出現環狀,那麼被檢測的模塊必定會出如今其依賴的模塊中。
             所以 23 行先記錄下依賴的模塊供 26 行判斷使用。第 24 行則僅僅是將日誌記錄到堆棧中。
    第 33 行:則是遞歸檢測被依賴的依賴
oop

下面這個日誌是當出現依賴循環時控制檯會打印出的信息。(提示:縮進就是經過深度實現的)

    以上就是 Hasor 中依賴排序的具體實現代碼,反應器類名爲:「ModuleReactor」。讀者能夠經過建立一個 Maven 工程加入下面代碼:

<dependency>
    <groupId>net.hasor</groupId>
    <artifactId>hasor-core</artifactId>
    <version>0.0.1</version>
</dependency>

經過命令獲取到 Hasor-core 的源碼: mvn eclipse:eclipse -DdownloadSources=true

----------------------------------------------------------------
目前的開發代碼存放於(包括Demo程序)
    Github:    https://github.com/zycgit/hasor
    git@OSC: http://git.oschina.net/zycgit/hasor

很是感謝您百忙之中抽出時間來看這一系博文。能夠經過Maven 中央倉庫網站  http://search.maven.org/ 搜索 Hasor 下載 hasor 的相關代碼。

相關文章
相關標籤/搜索