zoukankan      html  css  js  c++  java
  • 转:认识MyBean

    1初步体验

    我们先看一个框架自带的例子,以增加感性认识。打开samplessingleDEMO示例项目。这个示例演示了在一个EXE程序内,使用插件的概念调用两个窗口。其中包括一个主窗体 ufrmMain.pas文件,2 个作为插件的子窗体文件(Child目录),三个接口文件(Interface目录)。

     

    项目结构如下图:

                           

    我们先看主窗体

    主窗体的主要代码:

    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单元中,所以主窗体需要在引用列表中加入这个单元。

    主窗体调用接口非常简单,通过调用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接口)

    ② 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')。

    通过上述单元引用,框架运行所需要的环境就建立了。

    本章小结:

    要使用myBean框架,需要做以下几个步骤:

    主程序端

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

    2、在主程序项目文件(.dpr文件)的begin end 部分,添加applicationContextInitialize命令,初始化框架执行环境,载入必要的插件工厂(如果没有找到配置文件,将自动载入程序所在目录下的DLL插件和plugin子目录下的bpl插件);

    3、在需要引用插件的单元文件开头,引用mybean.tools.beanFactory单元,并用TMyBeanFactoryTools.getBean('TestDll') as Ixxxxxx (Ixxxxxx 为插件与主程序共同约定的接口)的形式调用插件。

    插件端(以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;

    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

  • 相关阅读:
    [置顶] app后端设计--总目录
    Centos6.5 nginx+nginx-rtmp配置流媒体服务器
    利用nginx搭建RTMP视频点播、直播、HLS服务器
    如约而至:微信自用的移动端IM网络层跨平台组件库Mars已正式开源
    使用pngquant命令近乎无损压缩PNG图片大小减少70%左右
    字符型图片验证码识别完整过程及Python实现
    python PIL Image模块
    app后端设计(12)--图片的处理
    linux 下如何抓取HTTP流量包(httpry)
    EHcache缓存框架详解
  • 原文地址:https://www.cnblogs.com/carcode/p/4634413.html
Copyright © 2011-2022 走看看