MyBean 框架入门手册<感谢[青铜]整理的如此细致和系统>
MyBean 框架入门手册
2014/9/15 by lighttop
目 录
第一章 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 搜索路径
这样,就已经安装好了!
第三章 认识MyBean
1、 初步体验
我们先看一个框架自带的例子,以增加感性认识。打开samples\singleDEMO示例项目。这个示例演示了在一个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 (插件名称) 方法调用,通过查找内部“登记簿”,获得插件的类别,并建立类的实例,转换成约定的接口。这就是框架工作的大致流程了。
当然,作为插件的子窗体,也应该有“与从不同”的自觉。因为它肯定要比普通的窗体对象承担一些额外的功能。当然,作为框架使用者来说,这种“不同”之处当然是越少越好,这样使用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。
根据上一节总结的关键点,我们先查看DemoConsole项目源代码
加入对mybean.console单元的引用,并调用applicationContextInitialize命令初始化框架。
1.2 在主窗口单元中, 引用mybean.tools.beanFactory单元
1.3 设计窗口,添加一个按钮
从上一节总结可知,点击按钮后,我们应该通过getBean方法获取插件实例,并调用其中的接口方法。我们假设要实现的插件将注册一个 “demoPlugForm”的名字,并实现了一个叫IFormShow的接口,该接口有一个 ShowAsModal的方法。
为此,我们书写以下的点击事件过程:
注意代码中 IFormShow和ShowAsModal 下面都有红色波浪线,说明IDE不认识这几个标识符。这是因为我们还没有定义它们。所以下步我们就要定义这些接口。
2、约定插件要实现的接口
从上一步主程序编写过程中,我们约定了一个叫IFormShow的接口,插件窗口也要有这个接口。所以我们单独建立一个单元,定义这个接口。
新建一个单元文件,并保存为 uIFormShow.pas,按下图输入IFormShow的定义。
注意上图中红框部分的GUID,可以通过按CTRL+ALT+G来生成。
然后,在第一步生成的主窗体单元中,uses 这个uIFormShow。
这样,主程序就算是制作好了。
为了满足好奇心,我们尝试编译并运行它,并点击按钮,出现以下错误消息:
这是很自然的,因为我们还没有实现这个插件嘛。那么下步就开始做插件吧。
3、 制作DLL插件
3.1新建一个DLL项目,保存为demoPlugDLL。
3.2 新建一个窗体,命名为ChildForm,作为DLL中的插件对象,以供主程序调用。我们保存为uChildForm。
3.3 让插件窗口实现约定的接口
作为插件要有被主程序掌控的自觉。它把自己能够实现的动作或功能,以接口的形式交给主程序备案,这个接口相当于供主程序掌控自己的“使用手册”。
第一步建立的主程序认为它“掌握”的插件会实现IFormShow接口。那么,我们的这个Child窗口只好按照主程序希望的样子来做了。由于IFormShow我们已经定义在uIFormShow.pas文件中,那么ChildForm窗体直接引用它,并继承和实现IFormShow接口。
在TFrmChild定义体内按Ctrl+shift+C,实现showAsModal方法:
注意:是self.ShowModal,而不是slef.ShowAsModal,不要看错了。
3.4 注册插件
作为插件bean,要有主动注册登记的良好品德,做一个开房登记身份证的守法好公民。
还记得注册登记的格式么?
beanFactory.RegisterBean('beanIDxxx',TBeanClassxxx)。
记得把带XXX的标识符改成实际的名称啊,这里只是样品哦。
记得写在哪里么?
对了,应该是在DLL项目文件里的begin...end中间,或是功能单元的initialization段。(说实话,我真的记不住initialization的拼写方式,所以还是复制一下吧)
下面是我们写好的:
可是红色波浪线怎么又来了?Delphi不认识beanFactory和RegisterBean?好吧,谁家孩子谁家管,先叫上它们的“家长”mybean.core.beanFactory,单元,放在uses列表里。
至此,我们的DLL插件就算完成了。编译吧,生成一个DLL文件。然后把它放到主程序所在目录。由于我们把两个项目放在同一目录下,所以默认编译生成的dll就在它该在的地方了。
注:如果一个DLL中有好多个插件,那么也可以在DLL项目文件中集中注册,首先在项目源文件中,添加对mybean.core.beanFactory单元的引用,这样就引入了mybean“注册官”,允许bean们注册自己的身份信息了。然后在begin .. End中间添加注册的语句。如下图所示:
4、运行调试
打开主程序,点“显示插件”按钮:
这个我们从零开始制造的主程序和插件就算完工了,并且如我们希望的一样运行了。是不是很有成就感?
第五章 MyBean的配置文件
第六章 插件之间的沟通
第七章 提高插件开发效率
MyBean 框架入门手册<感谢[青铜]整理的如此细致和系统>的更多相关文章
- Selenium自动化测试框架入门整理
关注嘉为科技,获取运维新知 本文主要针对Selenium自动化测试框架入门整理,只涉及总体功能及框架要点介绍说明,以及使用前提技术基础要求整理说明.作为开发人员.测试人员入门参考. 本文参考:Se ...
- 【Webpack】320- Webpack4 入门手册(共 18 章)(下)
介绍 1. 背景 最近和部门老大,一起在研究团队[EFT - 前端新手村]的建设,目的在于:帮助新人快速了解和融入公司团队,帮助零基础新人学习和入门前端开发并且达到公司业务开发水平. 本文也是属于[E ...
- 【Webpack】319- Webpack4 入门手册(共 18 章)(上)
介绍 1. 背景 最近和部门老大,一起在研究团队[EFT - 前端新手村]的建设,目的在于:帮助新人快速了解和融入公司团队,帮助零基础新人学习和入门前端开发并且达到公司业务开发水平. 本文也是属于[E ...
- jmeter 性能测试入门手册分享
深思熟虑之下,决定把这份性能测试入门手册分享给大家 最初整理这份教程的是因为自己在学习性能测试的过程中踩过了很多的坑,遇到了 数不清的问题,于是就想着将这些解决的问题全都归拢在一个文档里,方便自己查阅 ...
- 【原创】NIO框架入门(四):Android与MINA2、Netty4的跨平台UDP双向通信实战
概述 本文演示的是一个Android客户端程序,通过UDP协议与两个典型的NIO框架服务端,实现跨平台双向通信的完整Demo. 当前由于NIO框架的流行,使得开发大并发.高性能的互联网服务端成为可能. ...
- 【原创】NIO框架入门(一):服务端基于Netty4的UDP双向通信Demo演示
申明:本文由作者基于日常实践整理,希望对初次接触MINA.Netty的人有所启发.如需与作者交流,见文签名,互相学习. 学习交流 更多学习资料:点此进入 推荐 移动端即时通讯交流: 215891622 ...
- CodeIgniter框架入门教程——第一课 Hello World!
本文转载自:http://www.softeng.cn/?p=45 今天开始,我将在这里连载由我自己编写的<CodeIgniter框架入门教程>,首先,这篇教程的读着应该是有PHP基础的编 ...
- DPDK2.1 linux上开发入门手册
1引言 本文档主要包含INTEL DPDK安装和配置说明.目的是让用户快速的开发和运行程序.文档描述了如何在不深入细节的情况下在linux应用开发环境上编译和运行一个DPDK应用程序. 1.1文档总览 ...
- vue框架入门和ES6介绍
vue框架入门和ES6介绍 vue-mvvm模式,vue是一种轻量级的前端框架,主要为模板渲染,数据同步,组件化,模块化,路由等. https://cn.vuejs.org/ 源码:https://g ...
随机推荐
- [POST] What Is the Linux fstab File, and How Does It Work?
If you’re running Linux, then it’s likely that you’ve needed to change some options for your file sy ...
- iOS转让app-您必须移除要转让的 App 的所有构建版本和测试员,并清除“测试信息”下的所有信息字段解决方案
原文详细步骤篇: iOS App转让流程详情教程篇 此文为遇到的一个问题,及如何解决: 问题描述: 转让app遇到这个错误,如何解决? 不解决这个,app是无法进行转让的. 原因分析: 这个是由于Te ...
- @Transactional注解事务不回滚不起作用无效
写在前面 数据库Mysql8.0 添加@Transactional注解后事务并未起作用. 修改表的引擎后ok了.(详看下面转载内容) ================================ ...
- codeM 2018 资格赛
比赛链接:https://www.nowcoder.com/activity/2018codem/index?from=meituan 1.下单 给定若干商品,可以选择打折.满减两种方式. #incl ...
- [转]HTML DIV+CSS 命名规范大全
原文链接 常用DIV+CSS命名大全集合,即CSS命名规则 我们开发CSS+DIV网页(Xhtml)时候,比较困惑和纠结的事就是CSS命名,特别是新手不知道什么地方该如何命名,怎样命名才是好的方法. ...
- Pycharm 中添加第三方库和插件
在 PyCharm 中选择:File — Settings — 进入如下界面,点击 右上角的 “+” 可以添加其他库: 选择到相应的库,并 Install Package 即可:
- MATLAB 的unique函数——数组矩阵的唯一值
MATLAB 的unique函数——求数组矩阵的唯一值 相关MathWork文档见此:unique数组中的唯一值 1.C = unique(A) 返回与 A 中相同的数据,但是不包含重复项.C 已按照 ...
- JavaScript Window Navigator 浏览器本身的信息
window.navigator 对象包含有关访问者浏览器的信息. Window Navigator window.navigator 对象在编写时可不使用 window 这个前缀. Navigato ...
- React(0.13) 定义一个checked组件
<!DOCTYPE html> <html> <head> <title>React JS</title> <script src=& ...
- HTTP 请求头 Header
HTTP 请求头 Header HTTP请求头概述 (HttpServletRequest) HTTP客户程序(例如浏览器),向服务器发送请求的时候必须指明请求类型(一般是GET或者POST或者HEA ...