1、 初步體驗git
咱們先看一個框架自帶的例子,以增長感性認識。打開samples\singleDEMO示例項目。這個示例演示了在一個EXE程序內,使用插件的概念調用兩個窗口。其中包括一個主窗體 ufrmMain.pas文件,2 個做爲插件的子窗體文件(Child目錄),三個接口文件(Interface目錄)。程序員
項目結構以下圖:app
咱們先看主窗體框架
主窗體的主要代碼:網站
procedure TfrmMain.btnSingletonFormClick(Sender: TObject); //建立一個單實例窗體spa
begin.net
with TMyBeanFactoryTools.getBean('singletonDEMO') as IShowAsNormal do插件
begin日誌
showAsNormal;orm
end;
end;
procedure TfrmMain.Button1Click(Sender: TObject); //建立一個Bean窗體
begin
with TMyBeanFactoryTools.getBean('tester') as IUIForm do
try
showAsModal;
finally
UIFormFree;
end;
end;
注意上面紅色代碼部分,都有一個getBean方法,帶一個字符串參數表示插件名稱,調用後返回插件對象,而後經過as 操做,轉換爲對象支持的接口類型(對接口不熟悉的朋友請參閱相關知識)。上面這個getBean方法是TMyBeanFactoryTools 類的一個類方法。
TMyBeanFactoryTools 類自己定義在mybean.tools.beanFactory單元中,因此主窗體須要在引用列表中加入這個單元。
主窗體調用接口很是簡單,經過調用TMyBeanFactoryTools的getBean方法,傳入插件名稱便可返回插件對象,並訪問插件的方法。
這種以「插件」的方式調用子窗體,能夠看到主窗口並無引用(uses)子窗口單元文件。也就是說主窗體與子窗體實現了「解藕」。
那麼,getBean方法爲何能經過名字找到插件呢?按照猜想,子窗體應該經過什麼方法向框架系統進行了某種形式的「註冊登記」,這樣主窗體才能查找到。
因此,咱們接着看子窗體的實現。咱們打開其中的ufrmSingleton單元,它的窗體界面以下:
再查看它的代碼:
type
TfrmSingleton = class(TForm, IFreeObject, IShowAsNormal)
Memo1: TMemo;
private
{ Private declarations }
public
{ Public declarations }
procedure FreeObject; stdcall;
procedure showAsNormal; stdcall;
end;
看到如今,咱們尚未發現子窗體究竟作了什麼「註冊登記」的操做。不要急,在代碼最後,咱們發現了initialization段的代碼,這段代碼在單元剛載入時初始化。那麼這裏它作了什麼呢?
initialization
beanFactory.RegisterBean('singletonDEMO', TfrmSingleton);
beanFactory.configBeanSingleton('singletonDEMO', true);
咱們發現有一個 beanFactory 對象(實際是一個方法function beanFactory: TBeanFactory;),顧名思義,應該是一個bean工廠,專門生產bean(這裏也就是咱們要作的插件)。這個工廠類有一個RegisterBean方法,把插件註冊到某個「登記簿」中去。而'singletonDEMO'就是插件登記的名字,TfrmSingleton是插件的類型。這就驗證了咱們的猜想。
後面的beanFactory.configBeanSingleton('singletonDEMO', true),則是指明這個插件是個單例模式的插件,即只能創建一個對象實例。
那麼,這個beanFactory類型又是哪裏聲明的呢?查看一下,在 mybean.core.beanFactory;單元中。因此這個子窗體的uses列表中也有這個單元的名稱。
咱們暫且不去探究beanFactory的內部是如何工做的,先只要知道它在單元初始化部分登記了插件的類名稱,把它登記到了框架核心內部的一份「登記簿」中去。而後主程序窗體經過TMyBeanFactoryTools.getBean (插件名稱) 方法調用,經過查找內部「登記簿」,得到插件的類別,並創建類的實例,轉換成約定的接口。這就是框架工做的大體流程了。
主程序端 Uses mybean.tools.beanFactory; TMyBeanFactoryTools.getBean獲取插件實例 |
插件端 Uses mybean.core.beanFactory beanFactory.RegisterBean() 註冊插件類 |
登記的插件列表 |
固然,做爲插件的子窗體,也應該有「與從不一樣」的自覺。由於它確定要比普通的窗體對象承擔一些額外的功能。固然,做爲框架使用者來講,這種「不一樣」之處固然是越少越好,這樣使用myBean框架纔會不那麼繁瑣。
那麼,做爲myBean插件對象的窗體,與普通窗體的不一樣處到底在哪裏呢?要怎麼作才能成爲一個插件呢?
看TfrmSingleton 的定義:
TfrmSingleton = class(TForm, IFreeObject, IShowAsNormal)
這個窗體,在普通TFORM基礎上,實現了兩個接口IFreeObject和IShowAsNormal,這兩個接口分別定義了FreeObject方法和showAsNormal方法。那麼IFreeObject和IShowAsNormal自己定義在哪裏呢?
經過查找,咱們發現 IFreeObject定義在mybean.core.intf單元中,這是框架提供的核心單元,暫不去管它。
而IShowAsNormal定義在uIFormShow單元中:
IShowAsNormal = interface(IInterface)
['{4A2274AB-3069-4A57-879F-BA3B3D15097D}']
procedure showAsNormal; stdcall;
end;
(由於引用了這兩個接口,因此不要忘了uses mybean.core.intf和uIFormShow單元)
而FreeObject方法和showAsNormal方法的實現都很普通。freeObject就是調用self.Free 把對象自身free掉。而showAsNormal就是一個最普通不過的show()。
再回到主窗口的代碼,看它是如何調用這個子窗體的:
with TMyBeanFactoryTools.getBean('singletonDEMO') as IShowAsNormal do
begin
showAsNormal;
end;
咱們發現,'singletonDEMO'正是子窗口向框架註冊時用的名字,而IShowAsNormal 正是子窗口實現的接口之一。主窗口中也引用了這個接口文件,因此能夠經過這個接口調用方法,而無論子窗體到底是什麼類型的對象。
而實際上,myBean並不強制要求子窗口必定要實現某個特定的接口,你徹底能夠隨便設定子窗口要實現的接口。惟一的約定,就是主窗口和子窗口(做爲插件)之間都要遵循同一套接口,以便主窗口在經過GetBean得到子窗口對象後,可以轉型爲約定的接口並調用接口定義的方法。myBean是經過接口實現主窗體與插件之間溝通的。
固然,爲了開發的便利,框架約定了一個IFreeObject接口。若是插件實現了這個接口,則它就能夠本身管理生存期,而不須要程序員手動去銷燬。
在這個示例中,有兩個子窗體,其中一個TfrmSingleton實現了IFreeObject接口,另外一個TfrmTester則沒有實現這個接口,在使用後須要程序員手動釋放,見主窗體的代碼:
with TMyBeanFactoryTools.getBean('tester') as IUIForm do
try
showAsModal;
finally
UIFormFree; //手動釋(銷燬)插件對象
end;
這個UIFormFree方法也是IUIForm 接口中定義的,在TfrmTester中實現了這個接口方法:
procedure TfrmTester.UIFormFree;
begin
self.Free;
end;
上面囉囉嗦嗦分析了這麼多,其實總結起來就是如下內容:
主窗體端
① 引用mybean.tools.beanFactory (定義TMyBeanFactoryTools)
② 調用TMyBeanFactoryTools.getBean方法獲取插件對象
插件端:
① 引用:mybean.core.beanFactory (beanFactory類的RegisterBean,configBeanSingleton方法),mybean.core.intf (定義FreeObject接口)
② 二、調用:beanFactory.RegisterBean方法註冊插件,beanFactory.configBeanSingleton方法配置配件信息。
2、進一步探索
看完了幾個窗體的代碼,如今咱們再來查看這個項目的源代碼,看使用myBean框架還須要作些什麼準備工做。
program singleDEMO;
uses
Forms,
mybean.core.beanFactory,
mybean.console,
ufrmMain in 'ufrmMain.pas' {frmMain},
ufrmTester in 'Child\ufrmTester.pas' {frmTester},
uIUIForm in 'Interface\uIUIForm.pas',
ufrmSingleton in 'Child\ufrmSingleton.pas' {frmSingleton},
uIShow in 'Interface\uIShow.pas',
uIFormShow in 'Interface\uIFormShow.pas';
{R *.res}
begin
Application.Initialize;
registerFactoryObject(beanFactory, 'default');
Application.MainFormOnTaskbar := True;
Application.CreateForm(TfrmMain, frmMain);
Application.Run;
end.
注意上面紅色部分代碼,首先它引用了 mybean.core.beanFactory和 mybean.console兩個核心單元。而後在代碼執行部分註冊了一個工廠類的實例:registerFactoryObject(beanFactory, 'default')。
經過上述單元引用,框架運行所須要的環境就創建了。
v 本章小結:
要使用myBean框架,須要作如下幾個步驟:
l 主程序端
一、在主程序項目文件(.dpr文件)中引用 mybean.console (提供插件框架環境);
二、在主程序項目文件(.dpr文件)的begin end 部分,添加applicationContextInitialize命令,初始化框架執行環境,載入必要的插件工廠(若是沒有找到配置文件,將自動載入程序所在目錄下的DLL插件和plugin子目錄下的bpl插件);
三、在須要引用插件的單元文件開頭,引用mybean.tools.beanFactory單元,並用TMyBeanFactoryTools.getBean('TestDll') as Ixxxxxx (Ixxxxxx 爲插件與主程序共同約定的接口)的形式調用插件。
l 插件端(以DLL爲例):
一、創建DLL項目,在項目文件中引用 uses mybean.core.beanFactory單元,以提供註冊插件所需的工廠類;
二、在項目文件的begin .... end 段內,以beanFactory.RegisterBean('beanIDxxx',TBeanClassxxx)的方式註冊插件。其中TBeanClassxxx能夠是DLL中定義的任意類標識符(包括窗體),'beanIDxxx'是本身登記這個類時用的惟一標識符號;
三、上述第1-2步註冊插件的過程也能夠分散在DLL的各單元的 initialization 段。相關單元須要引用 mybean.core.beanFactory單元;
四、註冊的插件要實現與主程序共同約定的接口,以供主程序調用。
3、延伸閱讀(可選,不影響對框架的使用)
咱們先分析mybean.console單元的做用。既然它在uses後就起了做用,說明它在initialization 段裏執行了一些東東,因此咱們先去看這裏。
Initialization
{創建一個記錄運行日誌的TSafeLogger類型對象,並保存到__beanLogger全局變量中}
__beanLogger := TSafeLogger.Create;
__beanLogger.setAppender(TLogFileAppender.Create(False));
__beanLogger.start;
{創建一個TKeyMapImpl類型的對象,保存到 __instanceKeyMap}
__instanceKeyMap := TKeyMapImpl.Create;
__instanceKeyMapKeyIntf := __instanceKeyMap;
{主程序實例的上下文環境對象}
__instanceAppContext := TApplicationContext.Create;
{轉化成接口}
__instanceAppContextAppContextIntf := __instanceAppContext;
mybean.core.intf.appPluginContext := __instanceAppContext;
mybean.core.intf.applicationKeyMap := __instanceKeyMap;
appPluginContext.checkInitialize;
上面代碼的最後一行,是執行checkInitialize ,咱們繼續跟蹤它到底幹了啥:
procedure TApplicationContext.checkInitialize;
var
lvConfigFiles:String;
begin
if FFactoryObjectList.Count = 0 then
begin
checkReady;
lvConfigFiles := FINIFile.ReadString('main', 'beanConfigFiles', '');
if lvConfigFiles <> '' then
begin
if FTraceLoadFile then
__beanLogger.logMessage('從配置文件中加載bean配置', 'LOAD_TRACE_');
if checkInitializeFromConfigFiles(lvConfigFiles) > 0 then
begin
if FINIFile.ReadBool('main', 'loadOnStartup', False) then
begin
//加載DLL文件, 把DLL載入
checkInitializeFactoryObjects;
end;
end else
begin
if FTraceLoadFile then
__beanLogger.logMessage('沒有加載任何配置文件', 'LOAD_TRACE_');
end;
end else
begin
if FTraceLoadFile then
__beanLogger.logMessage('直接加載DLL文件', 'LOAD_TRACE_');
executeLoadLibrary;
end;
end;
end;
插件配置文件命名:主程序名+'.config.ini' 或 app.config.ini 。
若是存在配置文件,則FTraceLoadFile := True,不然FTraceLoadFile :=False;
1.3 官方資源
MyBean 由 D10.天地弦(QQ:185511468)開發。
官方Blog: http://www.cnblogs.com/DKSoft/
官方網站: www.diocp.org
討論QQ羣: 205486036 (MyBean輕量級配置框架)
MyBean的源碼庫: https://git.oschina.net/ymofen/delphi-framework-MyBean