zoukankan      html  css  js  c++  java
  • 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:VCLMyBean。如果用Git客户端,请将项目源码下载到上述目录(这里以D:VCLMyBean为例)。

    3、然后把D:VCLMyBeanSource添加到Delphi的libray 搜索路径

    image001

    image002

    这样,就已经安装好了!

    第三章 认识MyBean

    1初步体验

    我们先看一个框架自带的例子,以增加感性认识。打开samplessingleDEMO示例项目。这个示例演示了在一个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 'ChildufrmTester.pas' {frmTester},

      uIUIForm in 'InterfaceuIUIForm.pas',

      ufrmSingleton in 'ChildufrmSingleton.pas' {frmSingleton},

      uIShow in 'InterfaceuIShow.pas',

      uIFormShow in 'InterfaceuIFormShow.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的配置文件

    第六章 插件之间的沟通

    第七章 提高插件开发效率

  • 相关阅读:
    bzoj 1176 cdq分治套树状数组
    Codeforces 669E cdq分治
    Codeforces 1101D 点分治
    Codeforces 1100E 拓扑排序
    Codeforces 1188D Make Equal DP
    Codeforces 1188A 构造
    Codeforces 1188B 式子转化
    Codeforces 1188C DP 鸽巢原理
    Codeforces 1179D 树形DP 斜率优化
    git commit -m "XX"报错 pre -commit hook failed (add --no-verify to bypass)问题
  • 原文地址:https://www.cnblogs.com/DKSoft/p/3998057.html
Copyright © 2011-2022 走看看