程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Delphi >> MyBean 框架入門手冊<感謝[青銅]整理的如此細致和系統>,mybean手冊

MyBean 框架入門手冊<感謝[青銅]整理的如此細致和系統>,mybean手冊

編輯:Delphi

MyBean 框架入門手冊<感謝[青銅]整理的如此細致和系統>,mybean手冊


MyBean 框架入門手冊

2014/9/15 by lighttop

目 錄

MyBean 框架學習筆記..................................................................................................... 1

第一章  MyBean簡介...................................................................................................... 3

第二章 MyBean的安裝..................................................................................................... 4

第三章 認識MyBean........................................................................................................ 5

第四章 訪問DLL中的插件............................................................................................. 13

第五章 MyBean的配置文件............................................................................................ 19

第六章 插件生存期管理................................................................................................. 19

第七章 提高插件開發效率.............................................................................................. 19

第一章  MyBean簡介

1.1 概述

MyBean是一個用於Delphi應用程序開發的開源、輕量級、可配置插件框架。它通過巧妙的系統架構設計,無需復雜的配置和安裝,就可使程序開發人員方便地實現應用程序的模塊化開發、管理和發布,極大地提高軟件開發效率。並且,MyBean還留給程序開發人員最大的靈活性,沒有過多的約束條件,真正做到了簡便、靈活、高效的特點。

MyBean具有以下特性:

1、零依賴。綠色框架,編譯的應用程序無需依賴其他任何文件就可以享受框架提供的服務;

2、可配置。可以通過配置文件(json格式)設置插件文件(DLL或BPL)及其插件對象(稱之為bean)的配置信息;

3、熱插拔。插件文件(DLL或BPL)可動態加載和卸載,從而實現運行時更新插件目的;

4、顆粒性。只要實現了IInterface的對象(TObject及其子類)都可以作為插件對象。插件對象(bean)可以在單獨的DLL或BPL文件內,也可以在同一個EXE文件內;可以是一個復雜窗體,也可以只是一個簡單的對象。

5、生命周期管理。可以把插件對象簡單地設置為單實例對象,由框架接管插件的生命周期,自動管理生成和銷毀。

6. 完全開源。框架全部開源(支持D7 - XE7),並且遵循BSD協議,可免費用於商業軟件。

……

正是由於上述特點,MyBean給Delphi 程序員提供了一個插件式開發大型應用軟件的全新框架,必將成為最流行的DELPHI插件框架之一!

1.2 術語約定

 

Bean 本文檔用“bean”表示一個插件對象的概念。一個bean就是一個實現了某個約定的接口,並向框架插件工廠進行了注冊的類(及其實例)對象。

Plugin 本文檔用“Plugin”表示一個包含了一個或多個“bean”(插件對象)的DLL或BPL文件,是插件的宿主。

Console   本文檔用“Console”概念,表示一個可以載入不同的plugin(插件宿主文件),並調用其中的各種bean(插件對象)的主控程序。一般情況下可以簡單地理解為一個主程序EXE。

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

討論群:

第二章 MyBean的安裝

1、下載MyBean源碼包。可以到https://git.oschina.net/ymofen/delphi-framework-MyBean下載Zip壓縮包,也可以用Git客戶端下載。

2、將框架源碼壓縮包解壓到一個目錄,如D:\VCL\MyBean\。如果用Git客戶端,請將項目源碼下載到上述目錄(這裡以D:\VCL\MyBean\為例)。

3、然後把D:\VCL\MyBean\Source添加到Delphi的libray 搜索路徑

image001

image002

這樣,就已經安裝好了!

第三章 認識MyBean

1初步體驗

我們先看一個框架自帶的例子,以增加感性認識。打開samples\singleDEMO示例項目。這個示例演示了在一個EXE程序內,使用插件的概念調用兩個窗口。其中包括一個主窗體 ufrmMain.pas文件,2 個作為插件的子窗體文件(Child目錄),三個接口文件(Interface目錄)。

 

項目結構如下圖:

image003

我們先看主窗體

image004

主窗體的主要代碼:

procedure TfrmMain.btnSingletonFormClick(Sender: TObject);  //創建一個單實例窗體

begin

  with TMyBeanFactoryTools.getBean('singletonDEMO') as IShowAsNormal do

  begin

showAsNormal;

  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單元中,所以主窗體需要在引用列表中加入這個單元。

image005

主窗體調用接口非常簡單,通過調用TMyBeanFactoryTools的getBean方法,傳入插件名稱即可返回插件對象,並訪問插件的方法。

這種以“插件”的方式調用子窗體,可以看到主窗口並沒有引用(uses)子窗口單元文件。也就是說主窗體與子窗體實現了“解藕”。

那麼,getBean方法為什麼能通過名字找到插件呢?按照猜測,子窗體應該通過什麼方法向框架系統進行了某種形式的“注冊登記”,這樣主窗體才能查找到。

所以,我們接著看子窗體的實現。我們打開其中的ufrmSingleton單元,它的窗體界面如下:

image006

再查看它的代碼:

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 (插件名稱) 方法調用,通過查找內部“登記簿”,獲得插件的類別,並建立類的實例,轉換成約定的接口。這就是框架工作的大致流程了。

image007

當然,作為插件的子窗體,也應該有“與從不同”的自覺。因為它肯定要比普通的窗體對象承擔一些額外的功能。當然,作為框架使用者來說,這種“不同”之處當然是越少越好,這樣使用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接口)

② 2、調用: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 主程序端

1、在主程序項目文件(.dpr文件)中引用 mybean.console (提供插件框架環境);

2、在主程序項目文件(.dpr文件)的begin end 部分,添加applicationContextInitialize命令,初始化框架執行環境,載入必要的插件工廠(如果沒有找到配置文件,將自動載入程序所在目錄下的DLL插件和plugin子目錄下的bpl插件);

3、在需要引用插件的單元文件開頭,引用mybean.tools.beanFactory單元,並用TMyBeanFactoryTools.getBean('TestDll') as Ixxxxxx (Ixxxxxx 為插件與主程序共同約定的接口)的形式調用插件。

l 插件端(以DLL為例):

1、建立DLL項目,在項目文件中引用 uses mybean.core.beanFactory單元,以提供注冊插件所需的工廠類;

2、在項目文件的begin .... end 段內,以beanFactory.RegisterBean('beanIDxxx',TBeanClassxxx)的方式注冊插件。其中TBeanClassxxx可以是DLL中定義的任意類標識符(包括窗體),'beanIDxxx'是自己登記這個類時用的唯一標識符號;

3、上述第1-2步注冊插件的過程也可以分散在DLL的各單元的 initialization 段。相關單元需要引用  mybean.core.beanFactory單元;

4、注冊的插件要實現與主程序共同約定的接口,以供主程序調用。

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;


第四章 制作一個DLL中的插件對象

上一章我們分析的例子中,插件與主程序編譯在同一個EXE中,似乎感覺不出“插件”應有的樣子和作用。而且有些分析講得過於深入但又不夠透徹,第一次接觸MyBean的同學可能不容易理解。那麼,在這一章,我們將根據上一章總結的知識點,從頭開始制作一個主程序和一個供調用的DLL插件,以加深印象和理解。

1制作主程序

1.1 新建一個VCL Forms Appliction 項目,並保存。比如項目保存為DemoConsole,主窗體單元保存為ufrmMain.pas。

image008

根據上一節總結的關鍵點,我們先查看DemoConsole項目源代碼

image009

加入對mybean.console單元的引用,並調用applicationContextInitialize命令初始化框架。

image010

1.2 在主窗口單元中, 引用mybean.tools.beanFactory單元

image011

1.3 設計窗口,添加一個按鈕

image012

從上一節總結可知,點擊按鈕後,我們應該通過getBean方法獲取插件實例,並調用其中的接口方法。我們假設要實現的插件將注冊一個 “demoPlugForm”的名字,並實現了一個叫IFormShow的接口,該接口有一個 ShowAsModal的方法。

為此,我們書寫以下的點擊事件過程:

image013

注意代碼中 IFormShow和ShowAsModal 下面都有紅色波浪線,說明IDE不認識這幾個標識符。這是因為我們還沒有定義它們。所以下步我們就要定義這些接口。

2、約定插件要實現的接口

從上一步主程序編寫過程中,我們約定了一個叫IFormShow的接口,插件窗口也要有這個接口。所以我們單獨建立一個單元,定義這個接口。

新建一個單元文件,並保存為 uIFormShow.pas,按下圖輸入IFormShow的定義。

image014

注意上圖中紅框部分的GUID,可以通過按CTRL+ALT+G來生成。

然後,在第一步生成的主窗體單元中,uses 這個uIFormShow。

image015

這樣,主程序就算是制作好了。

為了滿足好奇心,我們嘗試編譯並運行它,並點擊按鈕,出現以下錯誤消息:

image016

這是很自然的,因為我們還沒有實現這個插件嘛。那麼下步就開始做插件吧。

3制作DLL插件

3.1新建一個DLL項目,保存為demoPlugDLL。

3.2 新建一個窗體,命名為ChildForm,作為DLL中的插件對象,以供主程序調用。我們保存為uChildForm。

image017

3.3 讓插件窗口實現約定的接口

作為插件要有被主程序掌控的自覺。它把自己能夠實現的動作或功能,以接口的形式交給主程序備案,這個接口相當於供主程序掌控自己的“使用手冊”。

第一步建立的主程序認為它“掌握”的插件會實現IFormShow接口。那麼,我們的這個Child窗口只好按照主程序希望的樣子來做了。由於IFormShow我們已經定義在uIFormShow.pas文件中,那麼ChildForm窗體直接引用它,並繼承和實現IFormShow接口。

image018

在TFrmChild定義體內按Ctrl+shift+C,實現showAsModal方法:

image019

注意:是self.ShowModal,而不是slef.ShowAsModal,不要看錯了。

3.4 注冊插件

作為插件bean,要有主動注冊登記的良好品德,做一個開房登記身份證的守法好公民。

還記得注冊登記的格式麼?

         beanFactory.RegisterBean('beanIDxxx',TBeanClassxxx)。

記得把帶XXX的標識符改成實際的名稱啊,這裡只是樣品哦。

記得寫在哪裡麼?

對了,應該是在DLL項目文件裡的begin...end中間,或是功能單元的initialization段。(說實話,我真的記不住initialization的拼寫方式,所以還是復制一下吧)

下面是我們寫好的:

image020

可是紅色波浪線怎麼又來了?Delphi不認識beanFactory和RegisterBean?好吧,誰家孩子誰家管,先叫上它們的“家長”mybean.core.beanFactory,單元,放在uses列表裡。

image021

至此,我們的DLL插件就算完成了。編譯吧,生成一個DLL文件。然後把它放到主程序所在目錄。由於我們把兩個項目放在同一目錄下,所以默認編譯生成的dll就在它該在的地方了。

注:如果一個DLL中有好多個插件,那麼也可以在DLL項目文件中集中注冊,首先在項目源文件中,添加對mybean.core.beanFactory單元的引用,這樣就引入了mybean“注冊官”,允許bean們注冊自己的身份信息了。然後在begin .. End中間添加注冊的語句。如下圖所示:

image022

4、運行調試

打開主程序,點“顯示插件”按鈕:

image023

這個我們從零開始制造的主程序和插件就算完工了,並且如我們希望的一樣運行了。是不是很有成就感?


第五章 MyBean的配置文件

第六章 插件之間的溝通

第七章 提高插件開發效率




  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved