本文將用盡量簡單的文字來描述插件框架原理。不少人覺得插件化很複雜,因此就一直將這類框架阻擋在門外。實際上,在咱們的實踐過程當中,從框架的使用角度來看,它很是簡單,咱們團隊裏面非正規院校畢業的女生也能夠來實際使用。若是說插件框架難的地方,我反倒以爲克服人的自然惰性更加困難。咱們不能習慣於墨守成規,日復一日年復一年,按照相同的模式來開發,將本身打形成一部「編碼機器」,成爲沒有價值的「程序猿/媛」。使用插件框架,沒有多少技術難點,不過須要咱們提高咱們的軟件開發思想,改變現有開發方式。html
1 插件框架本質程序員
在.NET平臺,一個程序是由「程序集 + 資源」構成的。程序集是由咱們開發的一個個的類。這些類多是通用功能的輔助類、數據訪問類、業務邏輯類,也能夠是WinForm應用的窗體類或者Web應用的Web窗體類。以傳統模式開發的程序,通常狀況下,不論是咱們開發的程序集,仍是引用的第三方程序集,它們都在應用程序的bin目錄下,以下所示。服務器
插件化開發方式與傳統方式不一樣在於,它會把程序集按照必定結構進行組織,好比下面這個程序是基於插件化方式來構建的,不一樣功能的程序集則組織到Plugins目錄下,以下所示。架構
此時,bin目錄則僅僅包含幾個很通用的程序集。框架
在Plugins目錄下,則按照功能進行分組,每個目錄爲一個插件,它實現一組功能。模塊化
下面咱們來看其中一個插件——AlarmManagementPlugin插件的目錄結構,以下所示,你會發現,插件擁有本身獨立的bin目錄,在本身的bin目錄下,放置着這個功能涉及的程序集。測試
在傳統開發方式中,放在bin目錄下的程序集會由.NET類加載器按需去加載,可是當咱們要實現插件化方式開發時,須要依賴於插件框架實現從不一樣的插件目錄中加載程序集。所以,插件框架本質上是擴展了.NET類加載器的功能,使其可以從插件目錄中加載程序集。優化
2 進一步看插件框架網站
插件化開發方式不只僅從程序集的組織方式上發上了變化,更重要的是,在功能的組織和實現也發生了變化。咱們用一個很是典型的分層架構看看兩者區別。編碼
下圖是一個分層架構的應用程序,由表示層、業務層、數據層等組成,每個層次都有相對應的功能組成,在表示層,咱們通常是構建了一個主界面,而後由不一樣的開發者在主界面上直接放置上菜單及菜單點擊事件的響應,同理,其它層次也相似,不一樣開發者根據須要實現的功能來添加不一樣的代碼。
在這種模式下,全部程序員開發的不一樣層次的功能代碼通常都在同一個程序集裏面,在開發過程當中,團隊須要不停的進行合併,並執行集成測試。下圖是你們都很熟悉的PetShop的項目結構。這裏面BLL程序集(項目)放置的是業務邏輯層的代碼,Web程序集(項目)放置的是表示層代碼,DALFactory及涉及的其它DAL程序集則是數據訪問層的相關代碼。
下面咱們來看看一個功能所涉及的程序集。這意味着,全部程序員都須要獲取到項目的代碼,而後遵循規則,在各個項目中添加本身的代碼,再將這些代碼合併起來,最後完整編譯再交付軟件系統。
如今咱們來看看使用插件化開發方式的組織方式,以下圖所示。
在這種模式下,功能被封裝到插件中,一個程序員負責若干個插件,每個插件由其相應功能涉及的界面、業務邏輯和數據訪問等代碼組成。每個插件能夠獨立開發、測試和部署,開發者間再也不須要對代碼進行合併。下圖是一個插件化應用程序的項目。
在這裏,每個插件完成一組功能,它們能夠有本身獨立的界面、業務邏輯和數據訪問實現,插件間具備物理隔離性,開發者能夠獨立開發本身的功能,獨立測試、部署與升級,一旦開發完成後,能夠由插件框架在進行組合,再也不須要進行代碼合併和總體發佈。
3 從簡單示例看插件化
如今咱們看看使用插件化來開發的示例。下圖是該軟件的集成後的界面,有5個一級菜單,其中「服務器」爲GPRS通信服務器插件定義,由程序猿A開發;「基礎數據、能耗分類、安裝數據」菜單爲基礎數據管理插件定義,由程序猿B開發。
下圖是「服務器」的子菜單和其中一個頁面。
下圖是「基礎數據」的子菜單和其中一個頁面。
該系統使用插件化開發方法以下。
(1)該系統使用了一個通用界面框架插件,你能夠從iOpenWorks網站來下載到該插件(http://www.iopenworks.com/Products/ProductDetails/Introduction?proID=386),這個界面框架插件提供了一個空白的界面,這個界面支持三級菜單,容許咱們經過如下配置將本身定義的菜單及菜單對應的窗體註冊到主界面。以下XML定義是由GPRS服務器插件來定義的。它配置了一個一級菜單和兩個二級菜單,以及對應的兩個用戶控件。
<Extension Point="UIShell.WpfShellPlugin.LinkGroups"> <LinkGroup DisplayName="服務器" DefaultContentSource="UIShell.EcmCommServerPlugin.CommServerUserControl"> <Link DisplayName="通信服務器" Source="UIShell.EcmCommServerPlugin.IntegratedCommServerUserControl" /> <Link DisplayName="測試服務器" Source="UIShell.EcmCommServerPlugin.CommServerUserControl" /> </LinkGroup> </Extension>
(2)程序猿A建立一個本身的項目來開發通信服務器,獨立的實現具體的應用和業務邏輯,其項目結構以下所示。在這裏,他分紅兩個項目來實現GPRS通信服務器,一個是界面表示層UIShell.EcmCommServerPlugin項目,另外一個是業務邏輯層UIShell.EcmCommServerService項目。在開發過程當中與程序猿B互相獨立,他們能夠獨立開發、測試、部署和維護。
運行這個項目後,程序猿A獲得如下的結果。
(3)同理,程序猿B也建立一個本身的項目來開發基礎數據管理,獨立的實現具體的應用和業務邏輯,其項目結構以下所示。在這裏,他分紅兩個項目來實現,一個是界面表示層UIShell.EcmConfigurationPlugin項目,另外一個是業務邏輯層UIShell.EcmDomainService項目,該項目能夠被A來重用。在開發過程當中與程序猿A互相獨立,他們能夠獨立開發、測試、部署和維護。
運行這個項目後,程序猿B獲得如下的結果。
(4)程序猿A和B開發到一個階段的時候,就能夠來隨時發佈本身的插件,點擊項目右鍵,直接將插件發佈到插件倉庫。
如下是發佈的結果,該項目的插件倉庫由3個項目組成。
通信服務器項目的插件列表以下。
配置管理項目的插件列表以下所示。
(5)測試人員/部署人員能夠經過插件管理來獲取到這兩個程序猿開發的插件,而後將這些插件下載組裝起來。
下載安裝後,就是以下的效果了。
4 插件化有什麼好處
從以上簡單的例子,咱們看到,插件化最直接的好處就是能夠以模塊化的方式來獨立並行構建軟件系統,在構建的過程當中能夠隨時進行集成。下面我把使用插件化的優勢總結一下:
(1) 插件化優化了團隊協做,避免團隊開發過程當中互相交叉,再也不須要更改各自的代碼將開發的成果集成到一塊兒;
(2) 使用插件化開發後,每個人的工做都很是的獨立,能夠有獨立的架構、獨立開發、獨立測試、獨立部署、獨立升級,並行構建;
(3) 插件化使得重用度更高,在咱們開發的一個項目中,超過50%的模塊都直接重用了原有的結果,不須要更改任何的代碼;
(4) 插件化使開發和維護更加簡單,這也是得益於每個開發人員能夠單獨開發本身的應用,每一個人都很專一,而且只須要關注系統中很小的一部分,在以上示例中,每個開發人員能看到的代碼都是本身來開發的;
(5) 使用插件化開發,咱們的發佈、升級也很簡單,右鍵發佈到插件倉庫,部署測試人員經過插件管理界面來下載每個人的成果,組裝成軟件,而且能夠隨時升級,這避免了不少手工發佈、集成;
(6) 使用插件化,能夠很容易的構建本身的知識庫,是徹底可複用,並持續不斷改進和增加。
5 插件框架原理
從上述小節,咱們看到,插件化開發更多的是軟件方法和思想上的改變。爲了知足這種開發方法的團隊協做模型,插件框架須要來解決一下幾個問題:
5.1 如何實現模塊化
實現模塊化,意味着咱們須要把功能相關的類組織到若干獨立的程序集,這些程序集是在插件目錄下。所以,實現模塊化就必須解決如下幾個問題:
(1)正確的類加載:即插件框架必須對CLR類加載進行擴展,使其可以從插件目錄中正確加載到插件程序集;
(2)插件描述:必須引入一個插件描述方法,它來告知當前插件的基本信息、版本標識、以及這個插件所包含的程序集;
(3)解決插件的依賴關係:在一個實際應用系統中,必然存在相互依賴。在這裏,依賴是指一個插件使用了另外一個插件定義的類型或者建立的對象。也就是說,咱們能夠直接使用另外一個插件定義的類型,在更加麻煩的場景中,還存在循環依賴。插件框架夠必須很好的處理好插件依賴關係的解析與管理。只有當全部的依賴關係都知足後,一個插件纔可以對外暴露功能;
(4)類加載空間定義:插件既有本身的程序集,也能夠依賴其它插件的程序集,那這時候就必須對插件的類型空間作一個惟一的定義,保證當前插件可以加載的類型獨立性和隔離性;
(5)插件多版本問題:因爲一個插件應用系統中的插件是由若干團隊開發,每個團隊獨立開發本身的插件,頗有可能一個插件使用了NLog的1.0版本呢,而另外一個團隊則更新到最新的1.1版本,這時候,咱們必須保證兩個團隊的插件可以正確運行;
(6)插件狀態的定義:插件能夠具有不一樣的狀態,並在不一樣狀態下有不一樣的體現。最經常使用的有:安裝、解析、正在啓動、激活、正在中止、卸載等,當插件處於解析狀態表示這個插件全部依賴關係都已經知足,能夠被正常啓動;當插件處於激活狀態則表示這個插件已經被啓動,全部功能都已經暴露了;
(7)插件啓動初始狀態和啓動順序:當插件被框架加載時,插件處於什麼狀態;用什麼樣的方式來設置插件的啓動順序;當插件有依賴關係時,這時候對啓動順序又有什麼影響;
(8)插件的初始化和終止處理:即至關於插件的入口和出口,當插件被啓動或中止須要執行的操做是如何來定義的。
5.2 如何實現模塊通信
插件框架必須解決模塊通信。傳統的通信,咱們都是基於CLR類加載來實現的,即咱們能夠直接引用另外一個程序集的類型,好比:ClassA cls = new ClassA();或者ClassA.Singleton.SayHello()。插件框架能夠提供兩種通信方式,以下:
(1)傳統通信方式:即咱們能夠像之前同樣,直接經過引用類型來進行通信,或能夠經過動態加載類型使用反射來通信;
(2)面向服務:基於SOA模型來實現通信,即服務 = 服務契約(接口) + 實現(類),服務提供商將服務註冊到總線,服務消費者使用服務契約從服務總線獲取服務,綁定使用。
5.3 如何實現模塊擴展
模塊擴展指的是在不更改已經發布的插件的任何代碼的狀況下,來變動該插件的功能。這也是插件重用的支撐之一。在OSGi.NET插件框架,使用了基於ExtensionPoint-Extension機制,暴露擴展點的插件能夠被擴展,擴展的插件經過XML來定義Extension的內容,從而被暴露擴展點的插件來獲取,並變動其功能。
5.4 插件框架高級支持
除了上述的三大基礎功能是插件框架須要實現的,它還能夠提供如下更高級的功能:
(1)對自動升級的支持:插件框架能夠支持插件的更新,當發現有新版本在插件框架內部時,能夠應用更新;此外,還能夠對插件框架自己實現自動更新;
(2)對動態性的支持:插件框架能夠支持動態安裝、啓動、中止、更新和卸載插件,容許靈活控制整個應用系統;
(3)對遠程管理的支持:能夠經過遠程管理的API,來實現插件內核的瀏覽和管理,從而實現內核狀況的查看和插件的遠程管理控制;
(4)對插件倉庫的支持:能夠將插件發佈到插件倉庫,而插件框架則能夠利用插件插件倉庫來實現動態安裝和更新,有利於應用系統的知識積累、發佈和團隊協做;
(5)對DevOps的支持:插件開發者能夠一鍵發佈更新,而測試人員和部署的應用,則能夠及時下載到更新。