這篇是承接《輕量級 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 的相關代碼。