基于 OSGi 的 Swing 客户端开发实践

IBMdW 发布于 2011/11/18 17:49
阅读 1K+
收藏 15

简介: 随着 OSGi 技术迅猛发展,插件化开发技术得到了更为广泛的关注,同时也涌现出了 Equinox、Felix 等众多基于 OSGi 规范的开源框架。但目前相关技术文章主要关注的是 OSGi 同 JavaEE 技术的结合,介绍 OSGi 与 JavaSE 相结合的文章较少。本文将介绍如何通过 OSGi 技术开发 Swing 客户端程序,从而使得基于 Java Swing 技术的客户端程序在秉承 Swing 技术诸多优点的同时具有 OSGi 的插件化架构。读者从中可以了解到如何通过 OSGi 规范的开源实现 Equinox,同 Swing 技术相结合开发 Java 客户端应用程序。

OSGi 技术概述

自从计算机软件诞生的那天起,如何开发“即插即用”的系统就是每一个开发人员心中的挥着不去的念头。从微软公司的 COM 组件技术到 Eclipse 的微内核 + 插件的架构,一直以来程序开发人员从未停止对应用程序插件化开发的追求。我们知道,Java 并不能真正的实现组件化编程(JSR294 正在着手进行这方面工作)。因而单纯依靠 Java 语言本身,是无法实现真正的插件化开发的。那么是否有其他方式能够优雅的解决这一问题呢?是的,通过 OSGi(Open Service Gateway Initiative)。

什么是 OSGi

OSGi 简单的说就是作为 Java 平台的一个模块层,目的是为了弥补 Java 平台本身对模块化支持的不足。如同 Java 最初是为机顶盒设计一样,OSGi 最初也并非针对企业级应用而是为智能家居领域所制定的基于 Java 构建动态化、模块化系统的规范。但在 Eclipse V3.0 选用 OSGi 作为平台规范之后,OSGi 得到了 Java 企业应用开发领域的广泛关注,众多业界知名的中间件和服务器产品采用 OSGi 作为其基础框架,其中包括 IBM Websphere,Oracle Glassfish 和 Spring DM Server 等知名产品。

OSGi 的实现机制

正如在 Web 应用服务器中,不同的 Web 应用具有完全独立的运行空间一样,OSGi 规范中定义每个 Bundle 都应该具有独立的 ClassLoader。OSGi 框架通过 Bundle 的元数据为其创建一个独立的类空间。Bundle 之间通过 Import-PackageExport-PackageRequire-BundleDynamicImport-Package等元数据信息进行 Bundle 信息的交互。此外,OSGi 规范还定义了其他有关 package 访问的一些策略,如版本,属性等。在 OSGi 框架加载执行 Bundle 前,会先对 Bundle 进行解析(Resolve),找到 Import-PackageRequire-Bundle等所指明的当前 Bundle 所依赖的其他 Bundle,并对 Bundle 版本、属性、依赖等进行分析处理及加载,从而保证 Bundle 在加载所需要的类时可以找到那些在 Import-PackageRequire-Bundle中依赖的其他 Bundle 中的类。

OSGi 的组成

通常,OSGi 框架从概念上可以分为三层:模块层、生命周期层和服务层。

模块层定义了 OSGi 中模块的基本概念,也就是 Bundle。其形式上和传统的 Java 中的 Jar 包相同,不过加上了元数据的定义。每一个独立的 Bundle 并不是一个完整的应用程序,而是一个逻辑上组件。一个 Bundle 能够引用其他的 Bundle,同时也能够被其他 Bundle 所引用。下一个小节会进一步介绍 Bundle 的相关概念。

生命周期层定义了在 OSGi 框架中如何对 Bundle 进行动态的管理,包括安装、启动、停止和卸载等。它为用户提供了一种动态管理应用程序组件的方式,用户可以随时添加移除某个模块,从而真正实现应用程序的动态可插拔。

服务层提供了当下流行的面向服务的模块间交互方式。通过使用基于接口的开发方法,某个 Bundle 可以作为服务提供者将数据作为服务进行注册和发布,同时其他 Bundle 作为服务的消费者可以使用其他 Bundle 提供的服务。


图 1. OSGi 层次结构
图 1. OSGi 层次结构

其中 OSGi 各层的主要功能如下:

  • Module Layer —— 模块层主要涉及包及共享的代码;
  • Lifecycle Layer —— 生命周期层主要涉及 Bundle 的运行时生命周期管理;
  • Service Layer —— 服务层主要涉及模块之间的交互和通信。

OSGi 的 Bundle

Bundle 是 OSGi 中的基本组件,其表现形式仍然为 Java 概念中传统的 Jar 包,同时通过 META-INF 目录下的 MANIFEST.MF 文件对其予以进一步的定义。通常一个 MANIFEST.MF 文件的内容如下所示:


清单 1. MANIFEST.MF 文件示例
 				
 Manifest-Version: 1.0 
 Bundle-ManifestVersion: 2 
 Bundle-Name: Util 
 Bundle-SymbolicName : com.ibm.director.la.util 
 Bundle-Version : 1.0.0.qualifier 
 Bundle-Activator : com.ibm.director.la.util.Activator 
 Bundle-Vendor : IBM 
 Bundle-RequiredExecutionEnvironment : JavaSE-1.6 
 Import-Package : org.osgi.framework;version="1.3.0"
 Bundle-ActivationPolicy : lazy 
 Export-Package : com.ibm.director.la.util;uses:="org.osgi.framework"
 Bundle-ClassPath : libs/jfreechart-1.0.13-swt.jar, 
 libs/jfreechart-1.0.13.jar, 
 libs/jfreechart-1.0.13-experimental.jar 

也就是说,MANIFEST.MF文件存储的实际上是 Bundle的元数据,元数据的内容能够精确的定义 Bundle的各种特征,同时能够更好的对 Bundle进行标识同时帮组用户的对 Bundle进行理解。例如,通过配置 Import-Package可以指明当前 Bundle 需要哪些其他的包,而通过配置Export-Package则可以表示当前 Bundle 的哪些package对外部可见。不难理解,通过这种方式可以有效的对 Bundle 内部和外部进行隔离。其他更多配置请参考相关资料。


图 2. Bundle 状态转换关系
图 2. Bundle 状态转换关系

此外,Bundle能够被动态地安装、启动、停止和卸载,具有完整的生命周期。其各个状态之间的转换关系如图 2所示。

Java 中的 Swing 客户端技术

Swing 技术是由原 Sun 公司发布的基于 Java 语言的客户端技术。其本身是一个由 Abstract Window Toolkit (AWT) 和 Java Foundation Classes (JFC) 所组成的轻量级组件集合。目前 Swing 技术已经成为 JavaSE 平台的一个重要组成部分,广泛应用在企业级应用开发中。Swing 提供了比 AWT 更好的显示效果,早期 AWT 的显示效果一直是 Java 客户端应用程序广受诟病的一个主要方面。Swing 支持外观的切换,通过模拟而不是使用原生组件(Native Component),使得 Swing 在保持较好应用程序外观效果的情况下,仍然保持了良好的平台独立性。但这也因此损失了一部分性能,这也是为什么 IBM 推出 SWT 的原因之一。不过,目前很多相关的测试表明,对于大部分应用,Swing 同 SWT 相比,并没有明显的性能差异。相反,Swing 所具有的内在的原生性以及不断改进的外观正逐渐吸引更多的客户端应用程序开发人员。

Swing 组件体系结构

Swing 是一个基于组件的框架。一个组件是一个乖巧与已知 / 指定对象的行为特征模式。 Swing 对象会异步地触发事件,且具有可描述其特征的属性,并对已知的一组指令进行响应。所有的 Swing UI 组件都是 Java Beans 组件,都遵循 Java Bean 组件体系结构规范。由于 Swing 的 JComponent 接口继承自 AWT 的 Container 类,因此从内部实现的角度看,Swing 的组件仍然依赖于 AWT 的容器。Swing 组件的类层次结构如图 3 所示。


图 3. Swing 组件体系结构
图 3. Swing 组件体系结构

Swing 中 MVC

Swing 库中大量使用了 MVC(Model-View-Controller)设计模式。Model 是代表组件状态和基本行为,负责管理自身状态并处理所有对状态的操作,Model 无须知道使用自己的 View 和 Controller 是谁,由系统负责维护它和 View 之间的关系,当 Model 发生了变化系统负责通知相应的 View。View 表示 Model 所包含数据的一个可视化展示。一个 Model 可以有一个以上的 View(Swing 中这种情况较少)。Controller 负责处理 Model 和用户之间的交互,当 Model 的状态发生了变化时 Controller 负责进行处理。


图 4. MVC 模型
图 4. MVC 模型

尽管通常 MVC 是用于设计整个界面的,但是 JFC 的设计者们却巧妙地将其应用在 Swing 中 Component 的设计上。每个组件都具有独立 Model、View 和 Controller,这大大降低了对象之间的耦合性。例如,Swing 中的 JTable 组件就有一个名为 TableModel 的 Model,用于描述 JTable 如何访问表格数据。

OSGi 同 Swing 的结合

随着 OSGi 技术不断发展,其优秀的插件化思想得到了更为广泛的关注,同时也涌现出了 Equinox、Felix 等众多基于 OSGi 规范的开源框架。除了 Eclipse 和其他一些知名厂商的中间件产品,基于 OSGi 的企业级应用多数基于 JavaEE 平台,讨论 OSGi 技术同 Java 客户端应用开发相结合的文章并不多。基于此,本文将通过一个日志分析程序 IBM Systems Director Log Analyzer(简称:SDLA)来说明如何采用 OSGi 技术开发具有良好插件化结构的 Swing 客户端应用程序。

为了便于说明和方便读者理解,SDLA 只实现了浏览日志文件、插件管理、图表化分析等基本功能,如图 5 所示。其中浏览日志文件用于打开待分析的日志文件,并将其注册为日志文件服务以供其他 Bundle 组件访问。插件管理功能能够对当前程序支持的 UI 组件进行动态的管理,包括启用和停止。图表化分析则是目前 SDLA 中已经实现的三个 UI Bundle,分别是 Bar Chart,Line Chart 和 Pie Chart,每一个 Bundle 都提供菜单和工具栏两种访问方式。也就是说,通过动态的启用和停止某个 UI Bundle,SDLA 能够动态地显示或隐藏 Bundle 对应的菜单和工具栏按钮。


图 5. SDLA 应用程序界面
图 5. SDLA 应用程序界面

显然,能够动态对系统内的组件进行插拔,能够方便的更新应用程序界面同时单个组件的故障不会对整个应用程序产生任何影响,这些特性对于构建高扩展性和健壮性的企业级应用来说都是至关重要的。那么,如何通过 OSGi 技术来构建插件化的 Swing 客户端应用程序呢?

在创建基于 OSGi 的客户端应用程序时,通常有两方面的问题需要考虑。

  • 服务提供方式;
  • 应用程序运行方式;

服务是 OSGi 中 Bundle 之间数据交互的主要方式之一,不同的服务提供方式具有不同的优缺点。本文将介绍常用的注册服务提供方式,以及 Declarative Service 方式。应用程序运行方式是指你的应用程序是否作为一组 Bundle 集合运行在某个 OSGi 框架之上,或者在你的应用程序中嵌入一个 OSGi 框架的实例,本文采用前者的方式。

为了更好的对 SDLA 应用程序的结构进行说明,这里首先介绍一下整个程序的结构组成,如图 6 所示。下面对各个 Bundle 的功能进行简单的说明。

  • com.ibm.director.la.host—— 负责提供整个应用程序的主窗体,也是整个应用程序的入口。它实际上充当的是整个应用程序宿主的角色,为其他 Bundle 所提供的动态菜单和工具栏按钮提供容器,同时将当前打开的日志文件作为服务发布给其他 Bundle;
  • com.ibm.director.la.service—— 其中定义了 SDLA 应用程序中所涉及的各个服务接口,以及某些基本的服务实现;
  • com.ibm.director.la.log4j—— 负责为整个应用程序提供日志服务;
  • com.ibm.director.la.target—— 这并不是一个 Bundle,仅用于提供 OSGi 运行环境所需的 Jar 包;
  • com.ibm.director.la.util—— 提供 SDLA 所需的常用工具类,例如文件操作,图表绘制等,因而这里也包含了对一些第三方类库的引用;
  • com.ibm.director.la.chart.bar—— 为应用程序提供可用于绘制柱状图的动态菜单和工具栏按钮;
  • com.ibm.director.la.chart.line—— 为应用程序提供可用于绘制折线图的动态菜单和工具栏按钮;
  • com.ibm.director.la.chart.pie—— 为应用程序提供可用于绘制饼状图的动态菜单和工具栏按钮。

图 6. SDLA 的程序结构
图 6. SDLA 的程序结构

了解了 SDLA 整个的程序结构之后,接下来将具体介绍如何实现插件化的菜单和工具栏按钮,以及 UI 组件的插件化原则。

Menu 的插件化实现

这里所提到的 Menu 的插件化是指应用程序的菜单可以根据当前 Bundle 集合中所提供的 Menu 服务而实现动态的添加和移除。为实现菜单的动态添加和移除,首先需要定义一个 IMenu 接口,其中对菜单的添加和移除方法进行了定义。


清单 2. IMenu 接口
 				
 public interface IMenu { 
 JMenu remove(JMenu menu); 
 JMenu add(JMenu menu); 
 } 

其中 add 方法用于添加菜单,remove 方法用于移除菜单。需要说明的是,两个方法的传入参数都是指当前正在操作的菜单项的上一级菜单。接下来,如果想在某个 Bundle 中提供一个菜单项服务,那么就可以定义子类实现 IMenu 接口。例如,如果想在 Bundle com.ibm.director.la.chart.pie中提供饼状图菜单,那么就可以定义一个 PieMenu 实现 IMenu 接口,具体的例子如清单 3 所示。


清单 3. PieMenu 菜单实现
 				
 public class PieMenu implements IMenu{ 
 private JMenuItem menuItem; 
 @SuppressWarnings("serial") 
 public PieMenu() { 
 menuItem = new JMenuItem(new AbstractAction("Pie Chart"){ 
 public void actionPerformed(ActionEvent e) { 
 } 
 }); 
 } 
 public JMenu remove(JMenu menu) { 
 menu.remove(menuItem); 
 return menu; 
 } 
 public JMenu add(JMenu menu) { 
 menu.add(menuItem); 
 return menu; 
 } 
 } 

由于篇幅的关系清单 3 中略去了部分代码,这里仅给出了类的主要结构。首先 PieMenu 中会定义一个 JMenuItem 的实例,用于提供实际的菜单功能。同时对 IMenu 接口的 add 和 remove 方法进行实现,通过调用 JMenu 的 remove 和 add 方法实现将当前实例中 menuItem 对象从传入的 JMenu 对象中添加或移除。

接下来,需要将前面介绍的 PieMenu 作为服务进行注册。通常可以在 Bundle 的 Activator 类的 start 方法中对服务进行注册,如清单 4 所示。


清单 4. 注册 PieMenu 服务
 				
public void start(BundleContext bundleContext)  throws Exception { 
 Activator.context= bundleContext; 
 Hashtable<String, String> properties =  new Hashtable<String, String>(); 
 context.registerService(IMenu.class.getName(),  new PieMenu(), properties); 
 } 

服务注册成功后,其他 Bundle 才可以使用此服务。接下来将介绍如何在宿主应用中引用 PieMenu 服务。首先,创建一个 IMenu 的默认实现类:DefaultMenu,用于 BundleContext 根据 ServiceReference 获取相应的服务,代码实现如清单 5 所示。


清单 5. DefaultMenu
 				
 public class DefaultMenu implements IMenu{ 
    private BundleContext context; 
    private boolean disposed = false; 
    private ServiceReference srvRef; 
    private IMenu menuService; 
    public DefaultMenu(BundleContext context,ServiceReference sRef) { 
       this.context = context; 
       this.srvRef = sRef; 
    } 
    public void dispose() { 
       if (disposed) { 
       context.ungetService(srvRef); 
       context = null; 
       srvRef = null; 
       menuService = null; 
    } 
 } 
 @Override 
 public JMenu remove(JMenu menu) { 
   if (context != null && !disposed) { 
      try { 
      menuService = (IMenu) context.getService(srvRef); 
      return menuService.remove(menu); 
      } catch (Exception e) { 
         e.printStackTrace(); 
        } 
  } 
 return menu; 
 } 
 @Override 
 public JMenu add(JMenu menu) { 
 if (context != null && !disposed) { 
 try { 
 menuService = (IMenu) context.getService(srvRef); 
 return menuService.add(menu); 
 } catch (Exception e) { 
 e.printStackTrace(); 
 } 
 } 
 return menu; 
 } 
 } 

此外,在宿主程序中还需要针对 IMenu服务创建特定的 ServiceTracker,即服务跟踪器,用于在当前 Bundle中对特定服务的生命周期进行跟踪。因此,SDLA应用程序中添加了 MenuServiceTracker类,继承自 org.osgi.util.tracker.ServiceTracker。其中实现了两个方法:addingServiceremovedService,源代码如清单 6所示。


清单 6. MenuServiceTracker
 				
 public class MenuServiceTracker extends ServiceTracker { 
   public MenuServiceTracker(BundleContext context) { 
     super(context, IMenu.class.getName(), null); 
   } 
  @Override 
   public Object addingService(ServiceReference ref) { 
      IMenu menu = new DefaultMenu(context, ref); 
      menu.add(Application.getInstance().getAnalysisMenu()); 
       return menu; 
   } 
  @Override 
  public void removedService(final ServiceReference ref, final Object svc) { 
     final DefaultMenu menu = (DefaultMenu) svc; 
     menu.remove(Application.getInstance().getAnalysisMenu()); 
     menu.dispose(); 
    } 
 } 

addingService 用于在将服务添加到当前 Bundle 时进行某些处理,与之相反,removedService 用于在将服务移除后进行一些清理操作。此处需要注意的是方法:Application.getInstance().getAnalysisMenu(),这个方法用来从应用程序主界面获取用于显示动态菜单项的上一级菜单,也就是动态菜单的容器。接下来最后一步就是打开前面定义的服务跟踪器,对 IMenu 服务进行跟踪,具体代码清单 7 所示。


清单 7. 开启服务跟踪器 MenuServiceTracker
 				
 menuServiceTracker = newMenuServiceTracker(Activator.getContext()); 
 menuServiceTracker.open(); 

通过前面介绍的一系列步骤,就可以将某个继承自 IMenu 的菜单项作为服务进行注册和发布,并在宿主 Bundle 中使用该服务。运行 SDLA 应用程序后,默认所有的三个 UI Bundle 所提供的菜单均可见,如图 7 所示。


图 7. 三个 UI Bundle 所提供的菜单
图 7. 三个 UI Bundle 所提供的菜单

接下来,可以通过在 OSGi 的命令行下输入命令 :stop BundleID 来将某个 Bundle 停止,从图 7 可以看出,饼状图对应的 Bundle Id 为 68,执行 stop 68,可以将饼状图所对应的菜单从应用程序的主界面移除,移除后的界面效果如图 8 所示。


图 8. 菜单动态移除后效果
图 8. 菜单动态移除后效果

Swing 组件插件化的策略

上一小节给出了如何将 Swing 的 Menu 进行插件化的实现步骤,本文接下来将通过介绍对工具栏按钮的插件化来分析 Swing UI 组件的插件化策略,即如何将这种插件化策略进行泛化,以适用其他 Swing UI 组件。SDLA 应用程序的三个 UI Bundle 在提供动态菜单的同时,还提供对应的工具栏按钮作为服务动态地发布。

首先仍然需要定义一个用于发布工具栏按钮的服务接口,接口提供 add 和 remove 两个基本方法。代码如清单 8 所示。


清单 8. IToolButton 接口
 				
 public interface IToolButton { 
 JToolBar remove(JToolBar bar); 
 JToolBar add(JToolBar bar); 
 } 

定义好服务接口后,接下来的步骤同 Menu 的实现基本类似。根据定义的 IToolButton 开发特定的 Button 实现代码,然后将其作为服务进行注册。完成 UI Bundle 部分的工作后,接下来在宿主 Bundle 中定义针对 UI 组件服务接口的默认实现,同时定义一个针对此服务的 ServiceTracker,最后在启动应用程序后启动这个 ServiceTracker。整个过程如图 9 所示。


图 9. OSGi 中 Swing 组件插件化实现步骤
图 9. OSGi 中 Swing 组件插件化实现步骤

Declarative Service 与 Swing 的结合

Declarative Service( 简称 DS) 规范是 OSGi 最新的部分。DS 的声明式服务允许通过元数据来定义和服务。通过 DS,开发人员可以定义 OSGi 服务而无需依赖任 OSGi 平台,例如可以将一个 POJO 定义为 OSGi 服务,这可以使得这些服务在测试时不依赖特定的 OSGi 平台。

在本文介绍的 SDLA 应用程序中,当用户点击某个图形绘制菜单或工具栏按钮时,应用程序会在主界面上打开一个新的选项卡,同时在面板上绘制指定的图形。但是各个 Bundle 之间是相互独立的,如果想在某个 UI Bundle 中向主界面中添加一个选项卡,那么就首先要获得主界面上的 JTabbedPane 实例。在本文中,将通过 Declarative Service 把这个 JTabbedPane 实例作为服务进行发布,以便其他 Bundle 可以访问它。

首先,在 Bundle com.ibm.director.la.service 定义一个新的服务接口 IComponent,代码如清单 9 所示。


清单 9. IComponent 接口
 				
 public interface IComponent { 
 public JTabbedPane getTabContainer(); 
 public void setTabContainer(JTabbedPane tabContainer); 
 } 

同时,在当前 Bundle 中创建一个 IComponent 接口的默认实现,代码如清单 10 所示。


清单 10. ComponentImpl
 				
 public class ComponentImpl implements IComponent { 
 private JTabbedPane tabContainer; 
 @Override 
 public JTabbedPane getTabContainer() { 
 return this.tabContainer; 
 } 
 @Override 
 public void setTabContainer(JTabbedPane tabContainer) { 
 this.tabContainer = tabContainer; 
 } 
 } 

从代码中可以看出,ComponentImpl 中定义了一个 JTabbedPane 内部变量,接下来在其他的 Bundle 中就可以对其进行赋值或取值。接下来,在 Bundle com.ibm.director.la.service 中新建一个名为 OSGI-INF 的文件夹,并创建一个新的 xml 文件,用于发布前面定义的 ComponentImpl 服务,文件内容如清单 11 所示。


清单 11. ComponentServiceProvider.xml
 				
 <?xml version="1.0" encoding="UTF-8"?> 
 <scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" 
 name="ComponentServiceProvider"> 
 <implementation class="com.ibm.director.la.service.impl.ComponentImpl"/> 
 <service> 
 <provide interface="com.ibm.director.la.service.IComponent"/> 
 </service> 
 </scr:component> 

其中元素 provide 中的属性 interface 用于声明当前服务的上层接口,元素 implementation 的 class 属性用于指出服务的实现类。需要注意的是,这里还需要在 Bundle 的 MANIFEST.MF 文件中添加一个参数配置:

  Service-Component: OSGI-INF/ComponentServiceProvider.xml 

运行整个应用程序并在 OSGi 命令行中输入命令:services,一切正常的话将可以看到正常发布的 ComponentImpl 服务。如清单 12 所示。


清单 12. Services 命令运行结果
 				
 {com.ibm.director.la.service.IComponent}=
 {component.name=ComponentServiceProvider, component.id=1, service.id=36} 
 Registered by bundle: com.ibm.director.la.service_1.0.0.qualifier [60] 
 Bundles using service: 
 No bundles using service. 

因为目前没有任何 Bundle 使用了此服务,因此会显示“No bundles using service.”。接下来将介绍如何在宿主 Bundle 也就是 com.ibm.director.la.host 中使用 IComponent 服务。首先,在 com.ibm.director.la.host 中定义一个针对 IComponent 的 Consumer 类,代码如清单 13 所示。


清单 13. ComponentConsumer
 				
 public class ComponentConsumer { 
 private static IComponent component; 
 public static IComponent getComponent(){ 
 return component; 
 } 

 // method will be used by DS to bind the Director log service 
 public synchronized void bindComponent(IComponent component) { 
 ComponentConsumer.component= component; 
 } 
 // method will be used by DS to unbind the Director log service 
 public synchronized void unbindComponent(IComponent component){ 
 if (ComponentConsumer.component== component) { 
 ComponentConsumer.component= null; 
 } 
 } 
 } 

正如 IComponent 服务不需要开发人员手动注册一样,ComponentConsumer 同样只需要进行简单的 xml 配置即可应用。在宿主 Bundle 中创建 OSGI-INF 文件夹,添加新的 xml 文件,文件内容如清单 14 所示。


清单 14. ComponentConsumer
 				
 <?xml version="1.0" encoding="UTF-8"?> 
 <scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" 
 name="host.ComponentServiceConsumer"> 
 <implementation class="com.ibm.director.la.service.consumer.ComponentConsumer"/> 
 <reference bind="bindComponent" cardinality="1..1"
  interface="com.ibm.director.la.service.IComponent" 
 name="host.Component" policy="static" unbind="unbindComponent"/> 
 </scr:component> 

然后在 BundleMANIFEST.MF文件中添加参数配置:

  Service-Component: OSGI-INF/ComponentServiceConsumer.xml 

运行整个应用程序后,这个 ComponentConsumer 类会被自动实例化。接下来,在 SDLA 的界面设计部分的代码里,我们就可以通过 ComponentConsumer 访问到 IComponent 服务,从而将主界面上的 JTabbedPane 实例赋值给 IComponent 服务中的对应变量。


图 10. 向主界面添加选项卡并绘制图形后的效果
图 10. 向主界面添加选项卡并绘制图形后的效果

同理,其他的 UI Bundle 也可以通过类似的方法访问到 IComponent 中的 JTabbedPane 实例,从而向其添加新的选项卡,进而绘制图形,显示效果如图 10 所示。

结束语

OSGi 服务框架能够帮助开发人员构建动态化、模块化的 Java 应用程序,在当前构建面向服务为中心的企业应用的过程中,OSGi 技术正发挥着越来越重要的作用。本文通过一个模拟的日志分析程序介绍了如何将 OSGi 技术同 Java Swing 客户端技术进行结合以开发插件化的 Java 客户端应用程序。读者从中可以了解到如何通过 OSGi 规范的开源实现 Equinox,同 Swing 技术相结合进行开发。

文章出处:IBM developerWorks

加载中
0
烟头
烟头

收藏了

developerworks怎么不支持RSS呢。。。。。

 

0
一号男嘉宾
一号男嘉宾
好文章,顶~
0
0
dh_
dh_
这篇文章不是人家IBM的嘛!
返回顶部
顶部