在軟件開發(fā)的領(lǐng)域中,插件技術(shù)一直是一項非常實用的技術(shù)。許多優(yōu)秀的軟件產(chǎn)品都提供了通過加載插件來擴展、豐富產(chǎn)品本身功能的能力。而像Firefox、Eclipse之類的軟件,更是將插件的功能發(fā)揮到了極致。順便做點廣告的是,我們的Mussel框架便是一套基于插件擴充應(yīng)用功能的框架:),在這里我們來分析一下.Net中常用的插件技術(shù)。
我們先從最簡單的加載方式來講起,我們知道在Assembly類中,提供了一個Loadxxxx的方法,這些方法可以讓我們從磁盤文件或是字節(jié)流中加載程序集到當(dāng)前的AppDomain中,例如:
Assembly.Load("Addin1.dll");
Assembly.LoadFile("Addin1.dll");
Assembly.LoadFrom("Addin1.dll");
AppDomain.CurrentDomain.Load("Addin1.dll");
上面的方式都可以將磁盤上的文件“Addin1.dll” 加載到當(dāng)前的AppDomain中,然后我們很自然的可以聯(lián)想到通過AppDomain.CurrentDomain.CreateInstance 或是 Activator.CreateInstance 之類的方法便可以構(gòu)建出插件具體的實例,一切似乎都非常順利。但是如果我們的插件服務(wù)是一個可熱插拔的,那么我們還需要考慮的是——我們?nèi)绾胃寮某绦蚣??Assembly是沒有提供UnLoad操作的,這就意味著我們只可以將文件或是字節(jié)流加載成當(dāng)前AppDomain中的Assembly,卻無法卸載這個Assembly,這明顯不符合“可熱插拔的”的要求。因此對于一些更換頻率較高的插件是不適合采用這種方式的——除非軟件的使用者可以接受頻繁的重啟軟件。
基于Assembly沒有UnLoad操作的缺陷,我們必須考慮通過其它的方式來實現(xiàn)來修復(fù)這個問題。通過瀏覽.Net類庫的一些聲明我們可以看到,在AppDomain中是有UnLoad操作的。這似乎是一個較好的解決途徑——我們可以創(chuàng)建一個新的AppDomain,將一些包含插件的程序集文件及相關(guān)的一些引用加入到這個新創(chuàng)建的AppDomain中,那么便可以實現(xiàn)插件的卸載了。當(dāng)然,要想實現(xiàn)這一構(gòu)想,我們必須對.Net的AppDomain有一個全面的認(rèn)識。
在介紹AppDomain之前我們希望大家對Remoting有一定的了解。因為.Net同進(jìn)程中的不同的AppDomain之間的通信,其實是基于Remoting的,采用的是Icp Channel。關(guān)于Remoting的具體內(nèi)容就不在本文的討論范圍之類,我們只需要知道:如果我們通過AppDomain.CreateDomain(string)方法創(chuàng)建出的一個新的AppDomain,其實只是一個Remoting的Proxy對象。我們知道在Remoting操作中,如果我們需要調(diào)用服務(wù)端的方法,服務(wù)相關(guān)的對象必須是從MarshalByRefObject這個類型繼承的;同時,如果需要在Remoting的客戶端與服務(wù)端之間傳遞信息,這個信息本身的對象必須是可以序列化的。
基于上面的理由,我們的插件實現(xiàn)類必須符合兩個規(guī)范:
根據(jù)上面的一些知識,我們就不難實現(xiàn)一個可卸載的插件加載模式:
今天先講理論,在下一章中,我們會演示相應(yīng)的代碼,敬請關(guān)注。