关于osgi.net ,想必大家也听说过,以下是自己在学习osgi.net 过程中整理出来的内容,供大家学习参与使用。
1. OSGI.NET 与UIOSP
OSGi是Open Service Gateway Initiative的缩写,Wiki对其的定义为:一方面它指OSGi Alliance组织,它是由IBM、Oracle、BEA、SAP等国际IT巨头组成的OSGi联盟组;另一方面指该组织制定的一个基于Java语言的服务规范——OSGi服务平台(Service Platform)。OSGi服务平台是一个动态的模块化框架:支持模块化与插件化、具有热插拔与动态特性、支持SOA、支持模块扩展、提供安全性支持等。
OSGi.NET插件框架提供了模块化与插件化、面向服务架构和模块扩展三大功能,适用于控制台、WinForm、Windows服务、WPF、ASP.NET和移动平台等任意.NET应用环境。
OSGi.NET是OSGi规范移植到.NET平台的服务规范。UIOSP是XAUI Open Service Platform缩写,即尤埃开放服务平台,它是OSGi.NET规范的实现,由西安尤埃信息技术有限公司提供。UIOSP完整实现了OSGi规范的移植,提供了模块化与插件化、面向服务架构支持、模块可扩展支持三大功能。UIOSP完善支持控制台应用程序、Windows服务应用程序、Windows窗体应用程序、WPF应用程序和ASP.NET应用程序等各种运行环境,并且支持Windows Mobile。
本文将让您体验使用Visual Studio.NET 2008 SP1 + SQLServer 2005 Express如何一步一步的基于UIOSP构建一个ASP.NET插件应用程序。目前UIOSP支持VS2005 SP1+/VS2008+/VS2010+开发环境,支持.NET Framework 2.0
1.1OSGI.NET 插件的程序结构:
基于OSGi.NET插件框架的应用程序体系结构如下图所示。在这里,应用程序由主程序和插件组成。主程序是具体的应用环境的入口,它用于启动OSGi.NET插件框架并进入插件框架暴露出来的入口点;应用插件则是实现软件不同功能的业务模块。
1.2 OSGi.NET插件框架优点:
- 开放、规范、统一:符合规范的模块均可被OSGi.NET插件框架加载。
- 快速集成:将模块放在插件模块即实现功能集成。
- 高度可重用:仅通过拷贝即可实现重用,无需修改任何代码。
- 热插拔与动态:每一个模块都可被动态安装、启动、停止和卸载。
- 标准模块化与模块物理隔离:模块具有统一规范且互相独立。
- 面向服务编程支持:提供基于面向服务架构编程模型以支持模块通讯。
- 动态可扩展:通过暴露扩展点实现动态扩展。
- 多环境支持:支持各种.NET应用环境。
2. OSGI.NET 模块化和插件化
2.1 模块化和插件化的特点:
- 物理隔离:基于UIOSP开发的模块是一个物理隔离的可单独部署的模块,每一个模块拥有独立的文件夹、类型空间、资源和类加载器。模块间互相独立互相隔离且互不影响。
- 高度可重用:模块的重用不需要再更改任何代码,只需要将模块拷贝到UIOSP指定的插件目录下,它的功能便向其它模块暴露。
- 规范化:模块具有统一的标准,每一个模块的目录结构、模块配置都是统一的,开发方法也完全一致。
- 快速集成:仅需要将模块都拷贝到指定的插件目录就能够实现模块功能的快速集成,无需再更改任何的代码。
- 易部署和升级:通过拷贝即可实现部署和升级。
核心思想是封装,即逻辑和物理兼顾的封装。逻辑封装,即OOP干的事儿,封装变化,面向接口编程。要真的应用在具体业务逻辑上时,是得需要花些功夫和实践的,对于OSGi的最佳实践来说,尤其重要。
物理 封装,即“物理隔离”,隔离的层次级别很低,到了具体的实体文件。首先,不像是普通的.NET程序,如WinForm,默认情况下,功能程序集文件都必须是在bin目录下,和主程序一块儿,如Web,文件都是在同一个目录层次中,核心逻辑也是在这个启动路径的bin目录中,他们是紧密联系的,无法分开存放的。OSGi的目录结构是可以自定义的,当然这些路径都是在一个已经定义好的范围之内,在这个范围内任意位置的程序集都可以被访问的到。比如你可以把主程序放在C盘,功能模块文件放在D盘。原理上讲,就是OSGi.NET改变了.NET默认的加载顺序和机制,允许程序动态加载指定路径的程序集。
其次,就是物理级别的“封装变化”,对于一个程序来说,每个子功能模块,根据需求的不同,对整体而言是可变的,也是不停变化的,以前只是将逻辑封装在一个程序集里,那么是否可以将多个关联程序集再封装在一个“模块”里呢?以前是内部的封装,现在是内部和外部一起封装,更抽象的封装。逻辑封装的表现形式是一个程序集,通常就是一个具体名字的dll,这里物理的封装就是一个具体名字的文件夹,在这个文件夹里包含了他所需的多个基本的程序集。理论上说,最佳场景应该是逻辑上、物理上与其他封装好的模块之间相互独立且隔离的,这样的话,我们能很简单的从文件结构上提炼出模块之间的关系以及整个软件功能的组成,这为以后的更新、维护、部署甚至重用都带来了好处。比如一个WinForm上的封装好的数据访问模块可以直接复制到一个Web上无需改动,即可使用,这也就是所谓的“高度可重用”,“快速集成”以及“易部署和升级”,他们依赖的就是比较好的逻辑和物理封装,还有可设置的加载路径
3.面向服务架构支持
面向服务的体系结构:SOA,也是OSGi.NET中一个重要功能,主要是为了各个模块可以以一种统一和通用的方式进行交互。以下是官方文档描述:
- 服务绑定模型:支持典型的“服务注册 – 服务搜索 – 服务绑定”的服务绑定模型。服务提供商想服务注册表注册服务,服务消费者搜索服务注册表并绑定需要的服务。
- 接口与实现隔离:每一个服务由“接口 + 实现”组成,接口相当于服务契约,而实现则是实现服务接口的具体类的实例。
这里的“服务绑定模型”,像是一个服务总线,用来存储和检索各种服务。而SOA就是将应用程序的不同功能单元通过定义良好的接口和契约联系起来,称为服务,它具有中立的接口定义,没有强制绑定到特定的实现上,松耦合。
硬件中的系统总线:
在OSGI.NET,它与硬件系统总线相似,差不多是同样一个目的,但更侧重于接口和具体实现的物理隔离,即所谓的“按需所用”,结合后面将提到的“即插即用”,将会使整个软件具有更好的灵活性和配置性,以适应未来各种不同的“改变”,无论外部或者内部。
4. OSGi.NET“服务总线”的实现
OSGi.NET的“服务总线”功能是以服务的形式发布的,有两种注册服务方式:
- 一个是在激活器当中,通过IBundleContext.AddService来添加一个指定接口和此接口实现的一个实例。
- 一个是在Manifest.xml当中,通过编辑器添加来完成。
服务注册完成后,可通过GetService来获取一个指定接口类型的此接口实现的所有实例或者通过GetFirstOrDefaultService来获取一个指定接口类型的此接口实现的第一个或默认实例。这两个方法可以通过激活器里的IBundleContext还有运行时环境BundleRuntime得到。
OSGI.NET 的服务总线可以用 IoC 容器来实现,基效果可能 会更好,OSGI.NET目前只提供一个较轻量级的解决方案,并没有引入 IoC 容器。
以下是服务总线关键部分的实现代码:
5. 模块可扩展支持
OSGI.NET 支持更高级的模块扩展策略,具体含义如下(官方文档):
- 扩展点:通过标准XML节点<ExtensionPoint>来定义一个模块向其它模块暴露的扩展点。暴露扩展点的模块会监听并处理其它模块对其的扩展。
- 扩展:通过标准XML节点<Extension>来定义一个模块对暴露扩展点的模块的扩展。这个XML节点会通过扩展点变更事件传递到暴露扩展点的模块。
- 动态扩展:模块在启动和停止时,会分别向平台注册和卸载相应的扩展点及扩展,该平台通过模块上下文暴露扩展点变更事件来处理动态的扩展信息。
- 零耦合:模块的扩展没有任何的耦合,仅通过标准XML来配置。
OSGi.NET的扩展点和扩展都是定义在相应模块的Mainfest.xml文件中的,非常容易维护和更改。模块与模块之间就可以完成“扩展点和扩展”,当然主程序中启动的运行时环境也能直接处理指定“扩展点”的“扩展”。
扩展点的定义,用 <ExtensinPoint> 节点来配置;
<?xml version="1.0" encoding="utf-8"?> <Bundle xmlns="urn:uiosp-bundle-manifest-2.0" SymbolicName="WorkspaceShell" InitializedState="Active"> <Activator Type="WorkspaceShell.Activator" Policy="Immediate" /> <Runtime> <Assembly Path="WorkspaceShell.dll" Share="false" /> </Runtime> <ExtensionPoint Point="ToolBar"/> <ExtensionPoint Point="MainMenu"/> <ExtensionPoint Point="Navigation"/> </Bundle>
对扩展点进行扩展,用 <Extension>节点来进行配置:
6. 热插拔与动态支持
OSGI.NET 支持热插拔与动态支持,具体内容如下()官方文档):
- 热插拔:所有的模块都可以被动态的添加和卸载。
- 生命周期:模块生命周期状态由“已安装、已解析、正在启动、已激活、正在停止、已停止、已卸载”组成,每一个生命周期状态下,模块提供的功能都可能不同。
- 动态:当模块执行任何生命周期操作时,模块会动态的想外界暴露或者隐藏它提供的功能,比如动态提供服务、扩展或者其它功能。
- 远程部署:支持模块远程部署,比如远程安装、启动、停止和卸载模块,或者订阅模块仓库中模块变更并同步。
7. 关于远程部署
OSGI.NET 部署的相关内容:
- 它是在线的,而不是离线,也就是不需要将程序关闭,用户可在正常情况使用下,部署模块
- 由于ASP.NET的特殊性,需要将“运行时”重启,但Web本身就是非持久性连接,处理恰当的话,对最终用户影响不是很大。WinForm不受影响
OSGi.NET对模块的处理并不是安装和卸载这么简单,它的生命周期如下:
这里“停止(Stopping)”和“卸载(Uninstalled)”需要稍加说明一下,停止是模块将自己的生命进程交还给运行时环境前最后的一个状态,而卸载是运行时环境将模块结束前的最后一个状态。简单的说,
安装、解析、卸载只能由运行时环境来管理,而启动、激活、停止是模块本身可一同参与的;停止的模块依然在运行时环境管理中,只不过模块不能再管理自己的状态,但其他模块可通过运行时环境对它进行控制;卸载的模块,在下次重启并删除之前,依然在运行时环境管理中;安装和启动,类似卸载和启动;解析和具体的模块加载过程,可参照前文“模块化和插件化”实例部分。
插件运行的完整过程(动态性)
8. 插件启动配置(Manifest.xml)
- “当框架激活时立即启动Bundle”,这个选项如果勾选上的话,则这个模块(Bundle)会按照安装、解析、启动、激活顺序执行。如果不勾选,则到安装后就停止了,如果这个模块还被其他模块依赖,则会忽略这个配置,直接运行到激活。还需要注意,每次更改这个选项时,须将此模块中的persistent.xml删除掉,否则无法看到效果。persistent.xml用来记录最后一次的初始化状态。
1.1 当然你可以通过编码的方式将它启动起来,例如
2. “晚激活,即当Bundle的类被加载是再激活”。这个有点儿类似.NET的晚激活策略,当存在“激活器”时,也就说如果勾选,则模块会从安装到解析便停止执行,而当别的模块需要引用它的某个类时会自动继续执行启动和激活,理论上可以减少不必要的消耗
9. 远程部署
- 整个要部署的插件文件夹压缩成zip包后删除这个文件夹。压缩包放到一个稍后可以访问到的位置,如D:\
2. 运行起整个主程序,默认的APE选项将不会出现。接着我们打开“远程管理工具”,输入字母l,这时也没有OSGi.NET.APEDecoderPlugin。保留着主程序不要关闭它。
3. 在远程管理工具里输入i "OSGi.NET.APEDecoderPlugin" "D:\OSGi.NET.APEDecoderPlugin.zip" "D:\cnblogs.com\OSGi.NET\Demo4\OSGi.NET.AudioPlayerShell\OSGi.NET.AudioPlayerShell\bin\plugins\FormatTypes\Lossless\OSGi.NET.APEDecoderPlugin",并回车,可以看到提示已经安装成功
4. 激活,OSGi.NET.APEDecoderPlugin处于安装(installed),而非激活(Active),还需要启动它,继续输入s 6
10.模块扩展支持
动态信息注入,优点:
- 被注入方不需要知道将会被谁注入,保证了逻辑的单一性,易于横向扩展
- 注入的协议的可配置型,基于XML的描述,可实现很方便的修改和维护
- 结合“接口加实现”的服务模式,可以快速整合各个模块的资源,实现了有效的“服务化”
模块扩展移除时如何将他的注入信息也一同移除?完整代码:
11. OSGI.NET 高级话题
11.1 OSGI.NET 的环境支持(官网文档):
- 支持控制台应用程序。
- 支持Windows窗体应用程序。
- 支持WPF应用程序。
- 支持Windows服务应用程序。
- 支持ASP.NET应用程序。
- 支持Windows Mobile应用程序。
- 支持UIOSP平台嵌套。
OSGi.NET是基于.NET框架且与语言以及类型无关,也就是说.NET能支持什么环境,OSGi.NET也就能支持什么环境,他能适应各种.NET生产和装配环境。
11.2 OSGI.NET 的设计原则与最佳实践
- 尽量逻辑隔离,物理隔离。逻辑隔离是必须的,低耦合、高内聚,物理隔离是最好的,可替换
- 尽量以“接口+实现”的方式来处理逻辑依赖,各模块之间的依赖越小越好
- 尽量把接口和实现的逻辑放在不同的模块中,结合动态性实现可维护、可更新
- 尽量以“扩展点和扩展”的方式处理模块之间的资源依赖,比如UI上的图标等尽量将共享的程序集放在一个共享模块中,避免重复依赖和版本混乱,也可减小模块体积
- 尽量不要在“激活器(Activator)”的start函数中调用服务实现,避免因模块启动顺序导致服务可能为空。其他地方如有需要尽量用
- UIShell.OSGi.ServiceTracker<T>来处理
- 尽量对具有逻辑或资源依赖的模块的关键生命周期状态进行相应的处理,比如安装,停止,卸载等,即所谓的“保持系统动态性”
- 尽量依赖另一模块的共享程序集而非整个模块,可有更快的加载速度
- 尽量将已有的模块封装打包到一个统一的部署平台,利于共享、测试、发布
UIOSP 官网:http://www.iopenworks.com/
OSGI.NET 官网:http://osgi.codeplex.com/
参考资料:http://www.cnblogs.com/zhaojudi/archive/2013/02/26/2933788.html
http://www.cnblogs.com/shalahu/archive/2013/02/21/2921306.html
示例源码: http://osgi.codeplex.com/SourceControl/latest