架构设计——OSGI规范-创新互联
OSGI(Open Service Gateway Initiative),即开放服务网关协议,是面向Java的动态模型系统。
OSGI是指由OSGI Alliance组织制定的Java模块化规范,OSGI规范的核心部分是一个框架,其中定义了应用程序的生命周期模式和服务注册。基于OSGI框架定义了大量的OSGI服务:日志、配置管理,HTTP服务(运行Servlet)、XML解析、设备访问、软件包管理、许可管理、用户管理、IO连接、Jini和UPnP等。
OSGI中文社区:http://www.osgi.com.cn/
OSGI官方网站:https://www.osgi.org/
OSGI框架实现了一个优雅、完整和动态的组件模型,组件(bundle)无需重新引导可以被远程安装、启动、升级和卸载。
OSGI服务平台提供在多种网络设备上无需重启的动态改变构造的功能。
为了最小化耦合度和促使耦合度可管理,OSGI技术提供了一种面向服务的架构,使组件动态地发现对方。
OSGI联盟已经开发了如HTTP服务器、配置、日志、安全、用户管理、XML等很多公共功能标准组件接口。标准组件的兼容性插件实现可以从不同计算机服务提供商得到。
OSGi的主要职责就是为了让开发者能够创建动态化、模块化的Java系统。
OSGI规范包括以下子规范:
A、Framework规范(OSGI核心,提供一个安全的可管理的Java Framework来部署可扩展的Java服务)
B、Package Admin Service规范(管理不同的Bundle之间的引用关系。当Bundle更新或者卸载时判断是否有其它的服务正在使用当前的Bundle)
C、Start Level规范(定义了启动和停止一个OSGi Service Platform时,不同的Bundles的启动或者停止的先后顺序)
D、Permission Admin Service规范(Bundle是否许可执行另外的Bundle的代码)
E、URL Handlers Service规范(怎样注册URL Schema,如何将java.io.InputStream对象转换为特定的Java对象)
F、Log Service规范
G、Configuration Admin Service规范
H、Device Access Specification
I、User Admin Service Specification
J、IO Connector Service Specification
K、Http Service Specification
L、Preference Service Specification
M、Wire Admin Service Specification
N、XML Parser Service Specification
O、Metatype Specification
P、Service Tracker Specification
Q、Measurment and State Specification
R、Position Specification
S、Execution Environment Specfication
OSGI的优势主要表现在以下几个方面:
A、热插拔的插件体系结构
基于OSGI的应用程序可动态更改运行状态和行为。在OSGI框架中,每一个组件都是可热插拔的,因此,对某一特定的组件的修改并不会影响到容器中的所有组件,运行中的大部分组件依旧能照常工作,部署一个新的Bundle时也不需要重新启动服务器。
B、可复用性
OSGI框架本身可复用性极强,易于构建真正面向接口的程序架构,每一个组件(Bundle)都是一个独立可复用的单元。基于OSGI独特的特性,进行企业开发的时候,对于新的开发,可以从企业的组件库中精简出可复用的模块,量身定做新的组件,大限度的利用了已有的资源,降低开发成本,增强企业的竞争力。
C、高效性,稳定性
OSGI是一个稳定而高效的系统。OSGI作为一个微核的系统,其核心只有为数不多的几个JAR包。基于OSGI框架的系统的低耦合性,其结构的优势性保证具体的某一个组件不至于影响到全局,更不会因为局部的错误导致全局系统的崩溃。
A、每个组件(Bundle)都由单独的类加载器加载,与一些Java EE项目中使用比较多的框架整合比较困难,如Spring MVC、Struts2等。
B、目前OSGI框架提供的管理端不够强大,现在的管理端中仅提供了基本的组件状态管理、日志查看等功能,像动态修改系统级别的配置(config.ini)、动态修改组件的配置(Manifest.mf)、启动级别等功能都尚未提供。
C、采用OSGI作为规范的模块开发、部署方式自然给现有开发人员提出了新的要求,需要学习新的基于OSGI的开发方式。
OSGI框架从概念上可以分为三层:模块层、生命周期层和服务层。
Module Layer:模块层主要涉及包及共享的代码;
Lifecycle Layer:生命周期层主要涉及组件的运行时生命周期管理;
Service Layer:服务层主要涉及模块之间的交互和通信。
OSGI Framework是OSGI Service Platform规范的核心组成部分,提供了一个通用的、安全可管理的Java framework。通过OSGI Framework可以支持一种叫做组件的Service application的部署和扩展。
OSGI兼容设备可以下载并且安装OSGI组件,也可一旦不再需要的时候删除。组件安装后会注册一定数量的Services,并被由同一个Framework下的其它组件使用。
在一个动态扩展的的OSGI环境中,Framework管理组件的安装和更新,同时也管理组件和Services之间的依赖关系。
Framework提供给组件开发者必须的资源来在Java平台上开发,为开发的组件提供了代码动态加载的功能, 也使得开发者开发、部署一个大规模的Services变的很容易。
其次,Framework为Java组件开发者提供了简明一致的编程模型,简化了开发部署的复杂性。编程模型允许开发者将自己的接口规范绑定到OSGI环境中的Service。
一个一致的编程模型帮助开发者可以应付一些可估计的危急错误。Framework将会运行在不同的硬件环境上,但一致的接口确保组件可以运行在一致的服务接口上。
模块层是OSGi框架中最基础的部分。
OSGi的模块化,是通过为Jar包添加metadata 来定义哪些类该暴露,哪些类该隐藏,其控制单元叫做组件Bundle(jar包)。
Bundle是以jar包形式存在的一个模块化物理单元,包含代码、资源文件和元数据(metadata),并且jar包的物理边界也是运行时逻辑模块的封装边界。
Bundle是OSGi中的基本组件,其表现形式仍然为Java概念中传统的Jar包。
通过META-INF目录下的MANIFEST.MF文件对其予以进一步的定义。
通常一个MANIFEST.MF文件的内容如下:
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Util
Bundle-SymbolicName: com.ibm.director.la.util
Bundle-Version: 1.0.0
Bundle-RequiredExecutionEnvironment: J2SE-1.5
Import-Package: org.osgi.framework;version="1.3.0"
Export-Package: com.ibm.director.la.util;uses:="org.osgi.framework"
Bundle-ClassPath: lib/junit.jar,
MANIFEST.MF文件存储的实际上是Bundle的元数据。
元数据的内容可以精确的定义Bundle的各种特征,同时能更好的对Bundle进行标识同时帮助用户对Bundle进行理解。
生命周期层在OSGi框架中属于模块层上面的一层,生命周期层的运作是建立在模块层的功能之上的。
生命周期层的主要功能是控制动态安装、开启、关闭、更新和卸载组件。
生命周期层能够从外部管理应用或者建立能够自我管理的应用(或者两者的结合),并且给了应用本身很大的动态性。
Bundle的使用需要生命周期层的API与OSGi框架的生命周期层进行交互。
OSGi服务是注册到OSGi框架中的一个Java对象。注册的时候可以设置Service的属性。而在获取Service的时候可以根据属性进行过滤。
Bundle可以通过Bundle的上下文去注册Service或去查询Service。
模块化是将一个大型系统分解为多个较小的互相协作的逻辑单元,通过强制设定模块之间的逻辑边界来改善系统的维护性和封装性。
模块(module)定义了一个逻辑边界,模块本身精确地控制哪些类是完全被封装起来的,而哪些类需要暴露出来作为外部使用。开开发者可以轻松地将实现屏蔽在模块的内部,将公共API暴露在外部。
面向对象编程中不会把所有功能都塞到同一个类。面向对象编程从问题域中发现多个事物,每个事物负责不同的功能,尽量做到高内聚和低耦合。面向对象的模块化粒度在类级别上。
OSGi的模块化是通过为JAR包添加metadata来定义哪些类应该暴露哪些类又隐藏在包中,其控制可见性的粒度是在组件(JAR包)层面上的。
OSGI模块化和面向对象思想所带来的能力都是通过控制可见性和可用性来保证高内聚和低耦合的,但粒度不同,一个是组件层面上,一个是对象层面上。
A、底层代码的可见性控制
Java提供了private、public、protected和package private(无修饰符)四种访问控制级别,提供了底层的OO数据封装特性。Packege具有分割代码的作用,但如果包中的代码要对包外可见,必须设置为public(protected,使用继承)。
org.serc.helloworld.Hello.java:定义了一个接口
package org.serc.helloworld;
public interface Hello {
void sayHello();
}
org.serc.helloworld.impl.HelloImpl.java:实现了Hello接口
package org.serc.helloworld.impl;
import org.serc.helloworld.Hello;
public class HelloImpl implements Hello{
final String helloString;
public HelloImpl(String helloString){
this.helloString = helloString;
}
public void sayHello(){
System.out.println(this.helloString);
}
}
org.serc.helloworld.main.Main.java文件:
package org.serc.helloworld.main;
import org.serc.helloworld.Hello;
import org.serc.helloworld.HelloImpl;
public class Main{
final String helloString;
public static void main(String[] args){
Hello hello = new HelloImpl(“Hello,SERC!”);
hello.sayHello();
}
三个文件分别在不同的包中。HelloImpl实现细节不应该暴露给其它包,但从Main.java的main方法中可以看出,为了创建Hello的实例,必须引入HelloImpl类,但是HelloImpl作为接口的实现细节,不应该暴露给使用者,违反了封装的原则。
如果不想让HelloImpl暴露出来,需要做额外的工作保证既隐藏了实现细节,又能简单的创建一个实现了Hello接口的实例。可以通过多种方法(比如工厂模式)来实现,但增加了与应用本身功能无关的多余工作,是Java的局限。
B、classpath的局限
在classpath中加入jar包的时候,只给出文件路径,而jar包的版本、一致性、依赖性,无法在classpath中明确的设置或是从classpath中看出相应属性。
classpath中的jar包是按序加载的,例如:
classpath=/servlet2.2/servlet.jar;/servlet2.3/servlet.jar, 在实际应用的过程中,Java使用servlet2.2,而不是servlet2.3。如果在大型系统中团队分开开发时,各用各的servlet包,并且版本号不一样,在最后将开发结果合并的时候,用的是哪个版本的servlet包就很难搞清楚。
即使classpath能注意到版本的问题,也没法精确指出依赖。开发者需要根据提示信息增加依赖包,直到虚拟机不运行到缺包异常为止。
C、OSGI对JAVA局限的改善
OSGi中很好解决了JAVA的局限性:
包的可见性:OSGi通过引入包的可见性机制,能够完全控制一个包中的代码对哪些模块可见,而不仅仅局限于无差别的可见性,从而完善了Java的代码访问控制机制。
包的版本:OSGi通过为包增加版本信息,可以精确控制代码的依赖,保证代码的版本一致性,弥补了classpath的缺点。
组件(bundle)是以jar包形式存在的一个模块化物理单元,包含了代码、资源文件和元数据(metadata),并且jar包的物理边界也是运行时逻辑模块的封装边界。
在标准的jar包的manifest文件中添加一些组件(bundle)的模块化特征(metadata)后,jar包就变成了一个bundle。
bundle和普通jar包大的区别就在于元数据。
Bundle元数据的目的在于准确描述模块化相关的bundle特征,让OSGi框架对bundle进行各种处理工作(比如依赖解析,强制封装等),元数据主要有三部分:
A、可读信息(可选)
OSGi标准定义了几个元数据条目帮助更好地理解和使用bundle,但所有的条目都不是必须的,并且不对模块化特性产生任何的影响,OSGi框架会完全无视可读信息。
Bundle-Name: SERC Helloworld
Bundle-Vendor: GR, SERC
Bundle-DocURL: http://elevenframework.org
Bundle-Category: example
Bundle-Copyright: SERC
B、bundle标识符(必须)
bundle标识符用于唯一的标识一个bundle。
早期的OSGi标准中并没有提供标识一个已知bundle的方法,直到OSGi R4标准,“唯一bundle标识符”被提出来。为了向后兼容,Bundle-Name不能用来作为标识符,否则就会增加维护向后兼容的工作,所以使用新的manifest属性Bundle-SymbolicName。Bundle-SymbolicName: org.serc.helloworld
Bundle-Name是给用户读的,而Bundle-SymbolicName是给OSGi框架读的,让OSGi框架能够唯一标识一个bundle。
只用一个Bundle-SymbolicName肯定是可以唯一标识一个bundle,但是随着时间的推移,bundle可能会有新版本,加入版本属性会让bundle的信息更加准确。
Bundle-Name: SERC Helloworld
Bundle-Vendor: GR, SERC
Bundle-DocURL: http://elevenframework.org
Bundle-Category: example
Bundle-Copyright: SERC
C、代码可见性(必须)
代码可见性用于定义内部与外部代码。在JavaSE中的jar包如果放在classpath里,那么jar包对classpath下的所有程序都是可见的,并且可见性不能改变。而OSGi标准定义了如下的属性用于描述代码的可见性:
Bundle-ClassPath:定义了形成bundle的所有代码所在的位置,Java 中的classpath是定义的jar包的位置,而Bundle-ClassPath属性描述的是bundle内部类在bundle中的路径。例如:Bundle-ClassPath:.,other-classes/,embedded.jar
Export-Package:显式暴露需要和其它bundle共享的代码,每个包之间用逗号分隔,每个包可用修饰词来修饰包的其它特征。
Export-Package: org.serc.hellworld; vendor=”SERC”,
org.serc.hellworld.impl; vendor=”Gou Rui”
Import-Package:定义bundle所依赖的外部代码,其格式和Export-Package相同,并且也可以使用修饰词来修饰包。修饰词是用来限制所依赖包的范围的,类似过滤器,而不像Export-Package中用来声明包的特征。例如:Import-Package: org.serc.helloworld; vendor=”SERC”
生命周期层在OSGi框架中属于模块层上面的一层,其运作建立在模块层的功能之上的。生命周期层主要功能是让开发者能够从外部管理应用或者建立能够自我管理的应用(或者两者的结合),并且给应用本身很大的动态性。
2、生命周期管理简介一般来说,程序(或者程序的一部分)都一定服从某种生命周期。软件的生命周期有4个典型的阶段,如下:
如果正在创建一个应用,首先得安装(install)应用;当应用的所有依赖都满足,可以执行应用;如果应用不再需要,可以停止(stop);一段时间后,可能需要更新(update)应用的版本;最终,可能会移除(remove)应用。
通过在外部或者内部对应用进行操作,完成对应用的生命周期管理过程。对于非模块化应用,操作是以整个应用为对象的;对于模块化应用,可以有更细粒度(针对应用中的某个模块)的生命周期管理。
要想使用组件(bundle),就得使用生命周期层的API与OSGi框架的生命周期层进行交互。
OSGi框架的核心并没有强制使用任何特定的API交互机制(比如命令行,GUI,或者XML配置文件等),只是单纯的Java API而已,开发者可以任意创造出自己想要的交互模式,保证了框架的灵活性。
在标准的Java编程中,会通过将jar包放到classpath中来使用jar包,而bundle不同。Bundle只有在被安装(install)到一个OSGi框架的运行实例中才能用起来,并且OSGi框架支持对bundle完整的生命周期管理,并且支持这些管理操作在应用执行完成,其动态性可见一斑。
Bundle生命周期的状态转移图如下:
可以通过Bundle的getState方法来获得bundle的当前状态。
Starting和Stopping状态是暂态,在持续一会儿后就会自动转移到下一个状态,不需要转移条件。
生命周期层的API主要由BundleActivator、BundleContext、Bundle三个核心接口组成。
A、BundleActivator
BundleActivator让开发者能够捕捉bundle的start和stop事件,并对作出自定义的反应。
BundleActivator的接口定义如下:
public interface BundleActivator {
public void start(BundleContext context) throws Exception;
public void stop(Bundlecontext context) throws Exception;
}
如果一个类实现了BundleActivator接口,那么类就成为一个Activator。但有实现是不够的,要让OSGi框架知道Activator的存在。所以还需要在MANIFEST文件中添加如下一项属性:Bundle-Activator:org.demo.Activator
当bundle启动(start)的时候,OSGi框架就会调用Activator的start方法,同样的也适用与stop方法。
并不是每个bundle都需要一个activator,有时候bundle只是为了和其它bundle分享代码,而并不需要在启动和停止的时候做出多余的动作。是否使用BundleActivator借口,需要具体问题具体分析。
B、BundleContext
BundleContext是bundle在框架中的执行时上下文,提供了与框架进行交互的方法。
BundleContext接口中的方法主要分为两类,一类与部署和生命周期管理相关,另一类是关于利用服务层进行bundle间交互的方法。
public interface BundleContext {
String getProperty(String key);
Bundle getBundle();
Bundle installBundle(String location, InputStream input) throws BundleException;
Bundle installBundle(String location) throws BundleException;
Bundle getBundle(long id);
Bundle[] getBundles();
void addBundleListener(BundleListener listener);
void removeBundleListener(BundleListener listener);
void addFrameworkListener(FrameworkListener listener);
void removeFrameworkListener(FrameworkListener listener);
}
BundleContext接口对于与其相关的bundle来说都是唯一的执行上下文,并且只有在bundle是属于active状态的时候执行时上下文才是有意义的,即在start方法被调用和stop方法被调用的两个时间点之间。如果一个bundle没有处于active时间段,但组件的bundlecontext对象被使用,框架会抛出异常。
框架使用这个上下文对象还有一个目的就是为了bundle的安全和资源分配,所以BundleContext对象应该被当做私有对象,不应该被随意在bundle之间传递。
C、Bundle
Bundle在逻辑上表示一个bundle,OSGi环境中的一个物理bundle对应一个bundle对象。bundle对象中包含了bundle的基本信息和bundle声明周期的控制接口。
在BundleContext接口中,getBundle方法可以得到Bundle对象。
对于每个被安装到框架中的bundle,框架都创建一个Bundle对象在逻辑上表达之。Bundle接口中定义了bundle生命周期管理的方法:
public interface Bundle {
BundleContext getBundleContext();
long getBundleId();
Dictionary getHeaders();
Dictionary getHeaders(String locale);
String getLocation();
int getState();
String getSymbolicName();
Version getVersion();
void start(int options) throws BundleException;
void start() throws BundleException;
void stop(int options) throws BundleException;
void stop() throws BundleException;
void update(InputStream input) throws BundleException;
void update() throws BundleException;
void uninstall() throws BundleException;
}
大部分OSGi框架的实现都是将locatioin解释为指向OSGi bundle的一个URL,在需要的时候就会通过URL将bundle下载到框架中来安装使用。但OSGi标准没有规定location的形式必须是URL,而且URL也并不是非要不可的,还可以通过输入流(Input Stream)来安装bundle。
bundle不能自己改变自己的状态,比如说一个active的bundle不能stop自己,stop自己就会抛出异常。
服务是服务提供者和服务使用者之间的一个契约。使用者一般不关心服务实现的细节,只关心是否满足契约(服务应该提供什么功能,满足什么格式)。使用服务的过程包含了发现服务和达成协议的形式,需要通过服务的标志性特征来找到对应的服务。
2、OSGI服务层服务层是OSGi框架中最上面的一层,服务层带来了更多的动态性,并且使用了面向服务编程模型。当一个bundle发现开始使用OSGi中的一个服务后,服务可能在任何的时候改变或者是消失。
OSGi框架有一个中心化的注册表,注册表遵从publish-find-bind模型:
一个提供服务的bundle可以发布POJO作为服务的实体;一个使用服务的bundle可以通过注册表找到和绑定服务。
可以通过BundleContext接口来完成服务的发布、发现、绑定,如下:
public interface BundleContext {
void addServiceListener(ServiceListener listener, String filter) throws InvalidSyntaxException;
void addServiceListener(ServiceListener listener);
void removeServiceListener(ServiceListener listener);
ServiceRegistration registerService(String[] clazzes, Object service, Dictionary properties);
ServiceRegistration registerService(String clazz, Object service, Dictionary properties);
ServiceRegistration[] getServiceReferences(String clazz, String filter) throws InvalidSyntaxException;
ServiceRegistration[] getAllServiceReferences(String clazz, String filter) throws InvalidSyntaxException;
ServiceReference getServiceReference(String clazz);
Object getService(ServiceReference reference);
boolean ungetService(ServiceReference reference);
}
3、发布服务为了让其它bundle能发现服务,必须在发布服务前对其进行特征描述。服务特征包括接口的名字(可以是名字的数组),接口的实现和一个可选的java.util.Dictionary类型的元数据信息。示例如下:
String[] interfaces = new String[]{StockListing.class.getName(), StockChart.class.getname()};
Dictionary metadata = new Properties();
metadata.setProperty(“name”, “LSE”);
metadata.setProperty(“currency”, Currency.getInstance(“GBP”));
metadata.setProperty(“country”, “GB”);
ServiceRegistration registration = bundleContext.registerService(interfaces, new LSE(), metadata);
上述代码中,通过ServiceRegistration对象可以更新服务的元数据:registration.setProperties(newMetadata);
可以直接将服务注销:registration.unregister();
ServiceRegistration对象不能和其它Bundles共享,因为ServiceRegistration对象和发布服务的bundle的生命周期相互依存。如果bundle已经不在框架执行环境中存在,那么ServiceRegistration对象也不应该存在。
代码中的参数new LSE()是一个POJO,不需要实现任何OSGi类型或者使用标注,只要满足服务约定就可以。
如果在删除发布的服务前bundle以及停止,框架会帮助删除服务。
可以根据服务约定从注册表中找到正确的服务。发现服务并获得其引用的接口如下:
ServiceReference reference =
bundleContext.getServiceReference(StockListing.class.getName());
reference是服务对象的间接引用,不直接使用服务对象是为了将服务的使用和服务的实现进行解耦,将服务注册表作为两者的中间人,达到跟踪和控制服务的目的,同时还可以在服务消失后通知使用者。
ServiceReference可以在bundle之间互享,与使用服务的bundle的生命周期无关。
在getServiceReference方法中,选择service的默认优先级是先选择service.rank最高的,在rank相等的情况下选择最早在框架中注册的。除了默认的规则,还可以在getServiceReferences中通过添加过滤参数(作为调用该方法的第二个参数)来做一些筛选。
ServiceReference[] references =
bundleContext.getServiceReferences(StockListing.class.getName(), “(&(currency=GBP)(objectClass=org.example.StockChart))”);
匹配参数是一个字符串,字符串的格式属于LDAP查询格式,在RFC1960标准中有完整的描述。
字符串中等号左边的内容是元数据(Dictionary)中的左值,通过左值对应的右值与服务的元数据进行匹配。匹配示例如下:
属性匹配:
(name=John Smith)
(age>=20)
(age<=65)
模糊匹配:(name~=johnsmith)
通配符匹配:(name=Jo*n*Smith*)
判断某个属性是否存在:(name=)
条件与:(&(name=John Smith)(occupation=doctor))
条件或:(|(name~=John Smith)(name~=Smith John))
*条件非: **(!(name=John Smith))
发现服务后,使用服务之前,必须从注册表中绑定实现的服务。
StockListing listing =
(StockListing) bundleContext.getService(reference);
返回的POJO实例和在注册表中注册的实例是同一个。
每次使用getService方法的时候,注册表会将对应服务的使用次数加1,同时会记录谁在使用该服务。如果不想使用服务的,注销服务。
bundleContext.ungetService(reference);
listing = null;
六、OSGI的实现
1、OSGI的具体实现OSGI是OSGi Alliance组织制定的Java模块化规范,但OSGI联盟并没有给出OSGI容器的实现,具体实现由第三方厂商完成,目前使用较多的OSGI容器有Apache Felix、Equinox、Spring DM。
2、OSGI的JAVA实现A、Apache Felix
Apache Felix是Apache旗下的一个OSGi框架,Felix是一个OSGi版本4规范的Apache实现。Apache Felix提供的服务几乎涵盖了全部的OSGi 4.2的标准,除此之外还提供了一些非标准的功能,例如iPOJO。Apache Felix框架本身非常紧凑,只需要3个包加一个shell就可以运行,无论是开发还是Debug都非常简便。
B、Equinox
Equinox是Eclipse旗下的OSGi框架,本身也被Eclipse采用,是Eclipse的PDE开发环境的底层。Equinox本身也是相当的全面的框架,提供的功能不比Felix少多少。Equinox被当做开发Eclipse Plugin的应用较多,如果要开发一个Web程序,就会感到功能和文档不够全面。Equinox大的优势在于和Eclipse结合紧密,只要安装了PDE,就已经有了Equinox,可以方便的在Eclipse里设置开发的Bundle,启动、部署等操作也异常简单,而且有专门的Debug界面。
C、Spring DM
Spring DM是Spring旗下的OSGi框架,Spring DM的大特点是结合了Spring框架。
D、Knopflerfish
Knopflerfish是OSGi的先行者,是一个相当标准OSGi框架,提供了绝大多数标准功能。
A、CTK Plugin Framework
CTK是基于Qt开发的支持生物医学影像计算的开源项目。
CTK中的CTK Plugin Framework模块借鉴了OSGI的思想,并实现了几乎完整的OSGI框架API。
B、C++ Micro Services
C++ Micro Services是基于OSGI思想的用于创建和管理模块化软件系统的C++库。
Apache Celix是基于C/C++的OSGI规范实现,提供了一个使用组件和面向服务编程(SOP)开发模块化应用的框架。
Apache Celix主要使用C语言开发,为了支持C++,以库的形式增加了抽象。
官方网站:http://celix.apache.org/
网页标题:架构设计——OSGI规范-创新互联
本文路径:http://azwzsj.com/article/pgdec.html