`

OSGi Bundle的构建策略及实践

 
阅读更多

软件编程发展到今天可以看作是一个量变引发质变的过程。最初,程序开发面向过程,开发人员需要编写大量的过程代码,随着过程代码的不断积累(量变产生),从代码维护和重用的角度,过程开发变得越来越不适应,质变产生,面向对象的开发逐渐被采用。由于面向对象的开发很好的封装了过程,而且从面向对象的角度可以很好的描述实际应用中的需求模型,因此面向对象的开发逐渐成为主流。同样,随着面向对象开发的不断应用(量变产生),出现了大量的可复用的类及包,维护这些类/包变得越来越困难,而且,尽管面向对象的编程机制可以很好的适应小规模应用的开发,但随着应用系统的规模越来越大,如同用细小的沙粒构建堤坝,面向对象的机制难于适应,质变产生,面向组件的开发被引入开发过程。面向组件的开发目前仍可以认为是处于一种探索状态,目前还不存在一种统一的标准可以遵循。

从另一个角度,历史是今天存在的基础,没有历史就没有今天。同样的道理,应用系统采用面向组件的开发实现,组件仍需要对象来构造,而对象在一定程度上是封装的功能过程,三者相辅相成。不管是从上而下还是从下而上,应用系统需求模型在其实现过程中,系统设计者应该充分关注组件、对象和功能过程三个层面。

OSGi可以看作是面向组件开发的一种思路和基础环境。在OSGi环境中实现应用系统的需求模型要求开发人员对组件、对象开发具有充分的理解。衡量一个应用系统实现好坏的标准之一是该系统是否是松耦合高内聚。面向对象的开发实现此目标的关键是接口/抽象的应用,OSGi面向组件的开发在此基础之上充分利用了对象类包封装的机制。

1. Bundle的构建策略

通常,当我们构建应用系统时,我们需要充分重视所构建应用的业务需求模型即领域模型,同样的,在用软件代码实现业务需求模型时,我们也应当对软件系统的架构模型给予充分的重视。采用OSGi技术实现应用系统时,展现在我们面前的将是一个个的Bundle组件,此时,我们必须首先弄清楚我们要构建什么样的Bundle。

1.1 明确Bundle的构建需求及实现粒度

从需求模型的角度看,Bundle可以看作是需求模型中的一个功能模块实现,因此,在开发Bundle之前系统开发人员必须明确Bundle的功能需求。需求模型中的功能模块的边界可大可小,同样,Bundle的实现粒度也是可大可小,极端情况下,小的Bundle可以仅实现一个微小的功能,大的Bundle可以实现整个业务系统。

以记录日志为例,如果记录日志仅输出到控制台,则一个类就可以实现整个的功能,如果记录日志输出到数据库系统,则开发人员需要在整个功能用一个Bundle实现,还是将日志记录功能和记录信息的数据库存储分为两个不同的Bundle实现,两个选择甚至更多的选择中做出决策。通常这个问题在现有的软件开发方式中不需要过多的重视,但在OSGi开发中,开发人员必须根据需求确定Bundle的实现粒度。

1.2 确定Bundle的类型

我们在面向对象的系统开发过程中,经常会将一些通用的功能设计成为工具类,多个工具类封装为一个工具类包。由于OSGi开发建立在面向对象的开发之上,Bundle的开发也存在这种特点。OSGi开发的另一个特点是服务机制的引入,一个Bundle可以将其提供的功能发布成为一个或多个服务,供其他Bundle查找使用。此外,建立在OSGi环境之上的组件开发也区别于通常的组件开发方式,因为我们可以利用OSGi环境提供的某些特性(参见下一节 充分利用OSGi环境的特性)。综上所述,我们可以将Bundle划分为如下几种类型(实际开发中,并不存在清晰的边界):

  • Utility Bundle

这种Bundle与通常开发方式中的工具类或工具类包没有本质的区别,仅仅是将这些工具类发布到OSGi环境中,这些工具类也不依赖任何的OSGi环境或特性。通常,这种Bundle特别适合目前存在的众多的第三方组件引入到OSGi环境中直接供OSGi开发人员使用。例如,我们可以将Apache开源项目Log4j发布的工具包通过修改其META-INF目录下的MANIFEST.MF文件,添加Bundle的元数据信息就可以直接将其封装为可供OSGi环境中其他Bundle使用的工具类Bundle。

  • 依赖OSGi特性的Bundle

这类Bundle可以被其他Bundle引用,为其提供功能,但是这类Bundle不能离开OSGi运行环境,否则不能使用。举例来说,一个提供数据缓存功能的Bundle可能使用Bundle的数据存储区来缓存数据。Bundle的数据存储区的位置对Bundle开发者来说是透明的(参见下一节 充分利用OSGi环境的特性)。如果将该Bundle的数据缓存功能迁移到OSGi运行环境之外,则必须修改实现添加对缓存区位置的处理功能。

  • 引用和(或)发布OSGi服务的Bundle

这类Bundle也可以认为是依赖OSGi特性的Bundle,唯一的区别就是,该类Bundle充分利用的OSGi中服务的特性,引用其他Bundle发布的服务和(或)向OSGi环境中发布自己的服务。OSGi环境提供的服务机制可以使得系统的实现遵循最大程度的松耦合。

1.3 隐藏Bundle的功能接口

组件开发除了应对系统的开发规模之外,其最重要的目标是降低系统耦合。开发人员采用OSGi Bundle开发时,应尽量屏蔽Bundle所提供功能的内部实现机制,仅将为其他Bundle提供的交互接口暴露出来。在OSGi中,这可以通过两种方式,一种是通过Export供其他Bundle引用的类包;一种是向OSGi环境发布服务。

在Equinox中,Bundle内部实现的类包通常以"internal"标注,如org.eclipse.equinox.internal.cm.reliablefile。标注为"internal"的类包通常不包含在Export列表中,或者通过"x-internal"属性标记(该属性仅在Eclipse开发环境中提供)。

隐藏Bundle功能接口的一种良好机制是通过OSGi服务。开发人员仅将自己开发的Bundle的接口类包发布给其他Bundle可见,同时,将功能接口的实现注册为OSGi中的服务,其他Bundle通过查找OSGi服务注册表获取该服务,通过服务的接口调用服务的功能。

1.4 尽可能应用OSGi提供的标准服务

OSGi联盟为一些经常用到的功能定义了标准服务,如应用程序管理服务,日志服务,事件服务,配置管理服务,用户管理服务,HTTP服务等等。如果开发人员采用的OSGi环境提供了上述服务的实现,且这些服务实现满足系统的需求,则开发人员应尽可能使用这些服务,而不是为这些类似的功能定义自己的实现。

2.充分利用OSGi环境的特性

2.1 OSGi环境的动态特性

OSGi是一个动态的环境,OSGi运行环境内部状态的变化通过事件发布/监听机制进行交互。开发人员在构建Bundle时应充分利用OSGi环境的事件监听机制。如,开发人员可以在自己的Bundle中注册Bundle事件的监听用来处理其他Bundle的安装,启动,停止,卸载等事件。

OSGi运行环境内部的事件主要包括三类:

  • 框架事件(FrameworkEvent)

STARTED 框架已经启动
ERROR 某个Bundle启动过程中引发错误
WARNING 某一Bundle引发一个警告
INFO 某一Bundle引发一个INFO类型的事件
PACKAGES_REFRESHED PackageAdmin.refreshPackage操作执行完成
STARTLEVEL_CHANGED StartLevel.setStartLevel操作执行完成

  • Bundle事件(BundleEvent)

INSTALLED Bundle被安装到OSGi环境后系统发布该事件
RESOLVED Bundle被成功解析
LAZY_ACTIVATION Bundle将被延迟激活
STARTING Bundle正在被激活
STARTED Bundle被成功激活
STOPPING Bundle被停止
STOPPED Bundle正在被停止
UPDATED Bundle被更新
UNRESOLVED Bundle被UNRESOLVED
UNINSTALLED Bundle被卸载

  • 服务事件(ServiceEvent)

REGISTERED 服务被注册
MODIFIED 服务被修改
UNREGISTERING 服务正在被注销

关于上述事件的详细信息参考OSGi API文档。

下述代码展示了如何在Bundle组件中通过实现FrameworkListener,BundleListener和ServiceListener接口,并使用BundleContext注册监听OSGi环境的各种事件:

package com.example;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.BundleListener;
import org.osgi.framework.FrameworkEvent;
import org.osgi.framework.FrameworkListener;
import org.osgi.framework.ServiceEvent;
import org.osgi.framework.ServiceListener;

public class Activator implements BundleActivator, FrameworkListener,
BundleListener, ServiceListener {

public void start(BundleContext context) throws Exception {

//注册监听
context.addFrameworkListener(this);
context.addBundleListener(this);
context.addServiceListener(this);
}

public void stop(BundleContext context) throws Exception {
}

//处理框架事件

public void frameworkEvent(FrameworkEvent event) {
if ((event.getType() & FrameworkEvent.ERROR) != 0) {
System.err.println("Framework ERROR: " + event.getBundle());
}
}

//处理Bundle事件

public void bundleChanged(BundleEvent event) {
if ((event.getType() & BundleEvent.STARTED) != 0) {
System.err.println("Bundle STARTED: " + event.getBundle());
} else if ((event.getType() & BundleEvent.STOPPED) != 0) {
System.err.println("Bundle STOPPED: " + event.getBundle());
}
}

//处理服务事件

public void serviceChanged(ServiceEvent event) {
if ((event.getType() & ServiceEvent.REGISTERED) != 0) {
System.err.println("Service REGISTERED: "
+ event.getServiceReference());
}
}

}

2.2 Bundle的生命周期及OSGi上下文特性

在Java虚拟机运行环境中,类的生命周期开始于虚拟机的加载,终止于被提前卸载或虚拟机停止,通常不需要开发人员干预。在OSGi环境中,开发人员可以参与Bundle的生命周期过程并能够获取Bundle在OSGi环境中运行的上下文。

通过实现org.osgi.framework.BundleActivator接口,并在Bundle的元数据头属性"Bundle-Activator"中指明该接口的实现类,如"Bundle-Activator: com.example.Activator"。当Bundle被启动或停止时,OSGi框架会将该Bundle的运行上下文BundleContext注入到该实现类实例中,开发人员可以在该Bundle的运行过程中使用该上下文访问OSGi环境信息,访问OSGi环境中的其他Bundle,注册事件监听,发布或引用服务等。

下面的代码片段展示了如何利用Bundle运行上下文与OSGi环境进行交互:

public void start(BundleContext context) throws Exception {
// 获取OSGi环境中的所有安装的bundle
for (Bundle bundle : context.getBundles()) {
System.out.println("Bundle Symbolic Name: "
+ bundle.getSymbolicName());
}
// 获取OSGi运行环境中的属性(查找范围包括系统属性)
System.out.println("osgi.framework = "
+ context.getProperty("osgi.framework"));
//注册其他Bundle
context.installBundle("file:C://test_bundle_1.0.0.jar");
//在该Bundle的数据存储区中构建datacache.file文件
context.getDataFile("datacache.file");
//注册监听
context.addFrameworkListener(this);
context.addBundleListener(this);
context.addServiceListener(this);
}

2.3 Bundle的资源管理特性

在通常的应用系统开发中,开发人员通常要考虑配置文件的存储路径,系统临时文件的存储路径等信息。由于OSGi环境中Bundle实际上是由一组文件资源构成,开发人员在开发Bundle时可以充分利用这种特点,使用Bundle存储与Bundle相关的配置信息。OSGi环境在运行时为每一个Bundle构建一个序列化数据存储区,开发人员可以使用该存储区存储Bundle在运行时生成的临时信息。

下述代码片段展示了如何通过Bundle提供的功能接口获取Bundle内部的资源:

  • context.getBundle.getEntry("") 只查询Bundle内部资源,返回Bundle的URL,该URL与实现相关,如:bundleentry://256/
  • context.getBundle.getEntry("/") 只查询Bundle内部资源,返回Bundle的URL,该URL与实现相关
  • context.getBundle.getEntry("/build.properties") 只查询Bundle内部资源,返回Bundle内部build.properties文件的URL,该URL与实现相关,如:bundleentry://256/build.properties
  • context.getBundle().getResource("build.properties") 使用Bundle的ClassLoader查询Bundle类域内的资源,如果Bundle不能被解析(RESOLVED),则只查询Bundle内部资源,返回Bundle内部build.properties文件的URL,该URL与实现相关。

开发人员在获取上述资源的URL后,可以转换成与Bundle运行系统相关的文件URL,如:file:C:/test_bundle_1.0.0。

下述代码片段展示了通过BundleContext接口获取Bundle运行时数据存储区资源:

  • context.getDataFile("") 返回该Bundle的运行时数据存储区位置,如:E:/worksapce/.metadata/.plugins/org.eclipse.pde.core/CobWeb/org.eclipse.osgi/bundles/256/data
  • context.getDataFile("config.xml") 返回该Bundle的运行时数据存储区内的config.xml文件,如果该文件不存在,则创建该文件。

2.4 面向服务的组件特性

OSGi的服务层提供了一个动态服务发布,绑定和查找模型,所谓的服务即指实现了某个(些)接口的Java对象。在面向对象的系统开发中,接口在分离系统的耦合方面扮演至关重要的角色,OSGi环境充分利用了这一点。

OSGi运行框架维护着一个服务注册表,Bundle可以通过Bundle上下文向该服务注册表中发布服务(只需指明服务实现的接口,服务的实例和服务的属性),也可以使用Bundle上下文从服务注册表中根据接口名称及服务的属性查找其他Bundle注册的服务。在此模式下,Bundle之间的耦合关系只存在接口层面,而与接口的实现无关。

下述代码片段展示了如何向OSGi服务注册表发布服务以及如何查找其他Bundle发布的服务:

public void start(BundleContext context) throws Exception {
//获取OSGi Log服务的服务引用
ServiceReference logSr = context.getServiceReference(LogService.class.getName());
//根据Log服务引用获取LogService服务实例
log = (LogService) context.getService(logSr);

//创建StringService接口服务实例
StringService ss = new StringServiceImpl(log);
//设定StringService的属性
Hashtable<string string> properties = new Hashtable<string string>(3); <br> properties.put(Constants.SERVICE_ID, StringService.class.getName()); <br> properties.put(Constants.SERVICE_VENDOR, "ACME"); <br> properties.put("PropName", "PropValue"); </string></string>

//使用BundleContext注册发布StringService服务
context.registerService(StringService.class.getName(), ss, properties);
}

3.Bundle的开发实践

实践1、构建Utility类型的Bundle

该示例将第三方log4j工具包(版本为1.2.13)封装为OSGi Bundle组件。操作步骤如下:

  • 步骤一:在C盘根目录下创建名称为"org.apache.log4j"目录,将log4j.1.2.13.jar文件拷贝到该目录中;
  • 步骤二:在"org.apache.log4j"目录下构建META-INF目录,并在此目录下创建名称为"MANIFEST.MF"文件;
  • 步骤三:编辑"MANIFEST.MF"文件,在此文件中添加以下内容:

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Apache Log4j
Bundle-SymbolicName: org.apache.log4j
Bundle-Version: 1.2.13
Bundle-ClassPath: .;log4j.1.2.13.jar
Export-Package: org.apache.log4j;version="1.2.13",
org.apache.log4j.config;version="1.2.13",
org.apache.log4j.helpers;version="1.2.13",
org.apache.log4j.jdbc;version="1.2.13",
org.apache.log4j.jmx;version="1.2.13",
org.apache.log4j.net;version="1.2.13",
org.apache.log4j.or;version="1.2.13",
org.apache.log4j.or.jms;version="1.2.13",
org.apache.log4j.or.sax;version="1.2.13",
org.apache.log4j.spi;version="1.2.13",
org.apache.log4j.varia;version="1.2.13",
org.apache.log4j.xml;version="1.2.13"

经过上述步骤后,名称为org.apache.log4j的OSGi Bundle已经构建完成。开发人员可以在其他Bundle中如同平常的开发模式一样引用Log4j的类包,获取Logger实例记录日志。

实践2、构建Service类型的Bundle

该示例Bundle由StringService接口,StringServiceImpl类和Activator类构成。该Bundle发布StringService接口服务,同时引用OSGi的标准LogService服务。

StringService接口是该示例Bundle发布的String服务实现接口。

package com.example;

public interface StringService {
String concat(String str1, String str2);
}

StringServiceImpl类是StringService接口的实现类,该类使用OSGi的标准LogService服务记录日志。

package com.example.internal;

import org.osgi.service.log.LogService;

import com.example.StringService;

public class StringServiceImpl implements StringService {
private LogService log;

public StringServiceImpl(LogService log) {
this.log = log;
}

public String concat(String str1, String str2) {
String str = str1 + str2;
if (log != null)
log.log(LogService.LOG_INFO, "Concat String Result is " + str);
return str;
}

}

Activator类为Bundle的生命周期入口,该类根据注入的BundleContext上下文获取OSGi LogService服务,然后注册StringService服务。

package com.example.internal;

import java.util.Hashtable;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceReference;
import org.osgi.service.log.LogService;

import com.example.StringService;

public class Activator implements BundleActivator {

private LogService log;

public void start(BundleContext context) throws Exception {
//获取OSGi Log服务的服务引用
ServiceReference logSr = context.getServiceReference(LogService.class.getName());
//根据Log服务引用获取LogService服务实例
log = (LogService) context.getService(logSr);

//创建StringService接口服务实例
StringService ss = new StringServiceImpl(log);
//设定StringService的属性
Hashtable<string string> properties = new Hashtable<string string>(3); <br> properties.put(Constants.SERVICE_ID, StringService.class.getName()); <br> properties.put(Constants.SERVICE_VENDOR, "ACME"); <br> properties.put("PropName", "PropValue"); </string></string>

//使用BundleContext注册发布StringService服务
context.registerService(StringService.class.getName(), ss, properties);
}

public void stop(BundleContext context) throws Exception {
}

}

Bundle的元数据清单如下:

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Example Plug-in
Bundle-SymbolicName: com.example
Bundle-Version: 1.0.0
Bundle-Activator: com.example.internal.Activator
Bundle-Vendor: EGRID
Eclipse-LazyStart: true
Import-Package: org.osgi.framework;version="1.3.0",
org.osgi.service.log;version="1.3.0"
Export-Package: com.example

4. 结论

通过本文的探讨,开发人员可以明确在OSGi环境下进行Bundle开发时所要面临的一些决策及注意事项。下一篇文章我们将要详细探讨OSGi环境下Bundle编程的细节。本文中的点击此处下载

分享到:
评论

相关推荐

    OSGi原理与最佳实践(完整版下载)

    5. **安全控制**:OSGi提供了细粒度的安全模型,每个bundle都可以有自己的权限策略,增强了系统的安全性。 在实践中,OSGi的应用场景广泛,包括嵌入式系统、企业级应用服务器、设备管理系统等。学习OSGi的最佳实践...

    OSGi原理与最佳实践(两章那种)与源代码

    OSGi(Open Services Gateway Initiative)...总的来说,掌握OSGi原理与最佳实践对于构建灵活、可扩展的Java应用至关重要。通过深入学习并实践这些内容,开发者可以提升系统设计能力,更好地应对复杂的企业级开发挑战。

    OSGi原理与最佳实践(完整版)&OSGi_in_action

    7. **最佳实践**:提供在实际项目中应用OSGi的建议,如如何设计模块化的系统架构,如何进行测试和调试,以及性能优化策略。 《OSGi in Action》可能涵盖: 1. **实战指南**:通过实际案例,展示如何将OSGi应用于...

    osgi相关文档、及学习资料

    它是开发人员编程OSGi应用时的重要参考资源,可以查看具体方法的功能、参数和返回值,了解如何正确使用OSGi API来构建和管理模块。 通过这些学习资料,你可以全面掌握OSGi的基本概念、API使用以及最佳实践,进一步...

    osgi介绍osgi介绍

    OSGi(Open Services Gateway Initiative)是一种Java模块化系统,它为开发人员提供了一种动态、模块化的...这些书籍通常会涵盖OSGi的配置、服务注册与查找、打包规范、安全策略以及如何在实际项目中集成OSGi等内容。

    OSGI原理与最佳实践(附简下载)

    **OSGI原理与最佳实践** OSGI,全称为Open Service Gateway Initiative,中文可译为开放服务网关倡议,是一个用于创建动态模块系统的技术规范。它最初是为了家庭网络环境中的服务集成而设计,但随着时间的发展,...

    spring-osgi-1.2.1.rar

    - 通过学习这些示例,开发者可以快速掌握Spring OSGi的基本用法和最佳实践。 8. **Spring OSGi的应用场景** - 适用于大型分布式系统,允许按需加载和卸载服务,降低系统复杂性。 - 在企业级应用中,如EJB替代...

    《深入理解OSGi:Equinox原理、应用与最佳实践》附赠光盘

    实践案例则可以帮助读者将理论知识应用到实际项目中,例如,如何创建和部署OSGi Bundle,如何使用OSGi的事件模型进行通信,以及如何利用Equinox的管理API进行系统监控和控制。 在最佳实践中,我们通常会学习如何...

    osgi hibernate

    8. **相关工具**:可能会提到一些辅助工具,如Apache Felix、Eclipse Equinox等OSGi实现,以及可能用到的构建工具(如Bnd或Maven的Bnd插件)来构建OSGi兼容的项目。 压缩包内的"OSGI 与Hibernate整合.docx"文件很...

    OSGI学习文档

    OSGI(Open Services Gateway Initiative)是一种开放标准,用于创建模块化...通过阅读这本书,你可以获得对OSGI的全面认识,从理论到实践,从而在你的项目中更好地利用OSGI的优势,构建更加灵活、可维护的Java应用。

    OSGI原理与最佳实践

    通过理解OSGI的基本原理和遵循最佳实践,开发者可以构建更灵活、可扩展且易于维护的Java应用程序。OSGI的广泛应用不仅限于服务器端,也可以用于桌面应用、嵌入式系统以及云计算环境,为现代软件开发提供了强大的模块...

    《OSGI原理与最佳实践》源代码.zip

    OSGI(Open Service Gateway Initiative)是一种开放的框架,主要用于创建模块化、动态的Java应用...这本书的源代码是实践OSGI技术的重要资源,对于希望构建动态、模块化Java应用的开发者来说是一份宝贵的参考资料。

    OSGi Web示例工程

    OSGi(Open Services ...学习和实践OSGi Web示例工程有助于理解如何在模块化环境中构建和管理复杂的Web应用,同时也可以提高软件的可维护性和可扩展性。如果你在搭建过程中遇到问题,记得留言讨论,以便获取更多帮助。

    OSGI Core PDF+Code

    在实际开发中,理解OSGI核心概念并熟练使用Equinox Bundle可以帮助开发者构建更灵活、可扩展的应用程序。通过使用OSGI,可以实现组件的动态部署,允许在运行时添加、修改或移除功能,而不会影响到其他部分。这在大型...

    To embed OSGi in servlet container

    OSGi是一种模块化系统,它允许Java应用程序以模块化的方式进行构建,提供了动态服务发现和依赖管理的能力。而Servlet容器,如Tomcat、Jetty等,是用于运行Web应用的地方,它们处理HTTP请求并调用相应的Servlet来响应...

    tomcat-osgi压缩包

    在提供的压缩包文件名称“Tomcat-OSGi-QuickStart”中,"QuickStart"通常表示这是一个快速入门或示例项目,帮助用户快速理解和实践如何在Tomcat中配置和使用OSGi。这个压缩包可能包含了预配置的Tomcat服务器,示例...

    关于OSGI分布式开发简单连接数据库

    OSGI(Open Services Gateway Initiative)是一种开放标准,用于创建模块化和可升级的Java应用程序。...正确理解和应用这些知识点,能帮助开发者构建出高效、灵活且易于维护的OSGI应用程序,实现数据库连接的无缝管理。

    OSGi-lib.rar

    6. **安全模型**:OSGi提供了细粒度的安全控制,每个bundle都可以有自己的权限策略。 学习和使用OSGi,开发者可以实现更加灵活、可扩展和可维护的Java应用程序。通过掌握OSGi的核心原理和实践技巧,可以有效地管理...

    osgi资料

    - 模块系统:详述OSGi的包和类加载机制,以及如何定义和管理模块(也称为bundle)。 - 依赖管理:解释如何声明和解决模块间的依赖关系,确保正确地加载和启动服务。 - 动态性:介绍如何在运行时安装、启动、停止...

    WPF界面-OSGI.net

    4. **WPF与OSGI的结合**:学习如何在WPF应用程序中嵌入OSGI框架,如何创建和管理OSGI bundle,以及如何通过WPF界面暴露和使用OSGI服务。 5. **动态性与可扩展性**:解释OSGI如何提供运行时的模块加载和卸载,使WPF...

Global site tag (gtag.js) - Google Analytics