OSGi原理与最佳实践:第一章 OSGi框架简介(5)Spring-DM

 由  ValRay 发布

1.3 Spring-DM

1.3.1 简介

Spring-DM 指的是 Spring Dynamic Modules。Spring-DM 的主要目的是能够方便地将 Spring 框架 和OSGi框架结合在一起,使得使用Spring的应用程序可以方便简单地部署在OSGi环境中,利用 OSGi 框架提供的服务,将应用变得更加模块化。具体的信息可以查看 http://static. springframework.org/osgi/docs/1.2.0-rc1/reference/html/。

1.3.2 环境搭建

可以从 Spring 的网站上下载新的 Spring-DM 包。目前新的版本为 1.2.0 RC1,下载地址在 http://www.springsource.com/download/community?project=Spring%20Dynamic%20Modules&version= 1.2.0.RC1 这个页面上。它提供了 with-dependencies 和没有 dependecies 两个包的下载。我们建议大家下载 with-dependencies 的这个包,这个包里面包含了 Spring-DM 依赖的其他的包。下载了这个 Spring-DM 包后,我们把压缩包中的 dist 和 lib 目录解压到硬盘上的某个目录,比如解压到 C 盘根目录下的 spring-dm 目录中。那么我们会在 C:\Spring-dm\dist 目录下看到如图 1-52 所示的内容。在C:\spring-dm\lib 中看到如图 1-53 所示的内容。

【图 1-52 Spring-DM 中 Dist 的内容 】

下面把 spring-dm 的 bundle 导入到 Eclipse 中。

我们首先导入 spring-dm 的包。打开 Eclipse,选择 Import…,在 Import 的对话框中选择 Plug-ins and Fragments(见图 1-54)。

【图 1-53 Spring-DM 中 lib 的内容 】

【图 1-54 导入插件 】
然后在 Import Plugin-ins and Fragments 对话框中做如图 1-55 所示的设置。

【图 1-55 设置要导入插件的目录 】
我们要设置 Plug-in Location,先设置为 c:\spring-dm\dist,导入 sprimg-dm 的包。点击 Next 后,出现了让我们选择导入的 Plugin 界面(见图 1-56)。

【图 1-56 选择要导入的插件 】
我们可以导入 core、extender、io 三个 Bundle。完成后,会在 Eclipse 的工作区看到如图 1-57 的显示。

【图 1-57 导入后的显示 】

我们直接在 Run Configurations 中选择这三个 Bundle,并执行,会发现三个 Bundle 都是 INSTALLED状态,如果我们启动Bundle,会报错,原因是我们没有加入这三个Bundle所依赖的Bundle。而这些Bundle,也就在c:\spring-dm\lib目录下。我们用和前面一样的方式来导入lib中所需要的Bundle。要 导 入 的 Bundle 是 com.springsource.org.aopalliance 、 org. springframework.aop 、 org.springframework.beans 、 org.springframework.context 、 org.springframework.context.support 、 org.springframework.core。

这个时候,在 Eclipse 的工作区看到的应该是如图 1-58 的显示。

【图 1-58 导入其他需要的插件 】

在 Run Configurations 中选择目前导入的所有 Bundle,然后再选择 Target Platform 中的 org.apache.commons.logging 这个 Bundle,运行。可以看到,所有的 Bundle 都是 ACTIVE 状态了(见图 1-59)。

到这里就完成了环境的搭建。下面就来做一个基于 Spring-DM 的应用。

【图 1-59 启动后的 Bundle 状态 】

1.3.3 HelloWorld

先看一个简单的例子。在这个例子中将展示如何把一个Spring Bean展示成服务,以及如何把OSGi 的服务注入到 Spring 的 Bean 中。在这个例子中,有两个 Bundle,分别是 HelloWorld Bundle 和 TimeService Bundle。例子代码见源码 Spring-DM 中的 HelloWorld 工程和 TimeService 工程。

这两个工程中都没有 BundleActivator。在 Bundle 启动和停止的时候不用去做什么事情。先来看 一下 TimeService 工程(见图 1-60)。

【图 1-60 TimeService 工程 】

可以看到,我们在 TimeService 工程中定义了 TimeService 接口和 TimeService 的实现(TimeServiceImpl)。在这里,这样定义和实现并不是一个很好的做法,接口的定义应该放到一个单独的 Bundle 中,这里不过是为了示意,选择了一个简单的做法。TimeService 接口和 TimeServiceImpl 类的代码很简单,我们不再介绍。这个工程大的不同是在 META-INF 目录下有一个 spring 目录。这个目录中有两个文件,分别是 timeservice.xml 和 timeservice-osgi.xml。我们分别看一下这两个文件的内容。

timeservice.xml

<?xml version="1.0" encoding="UTF-8"?> 
<beans xmlns="http://www.springframework.org/schema/beans"   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">   
  <bean name="timeService" 
class="org.osgichina.demo.timeservice.impl.TimeServiceImpl"> 
  </bean> 
</beans>

这个 xml 文件中定义了一个 spring bean。这个和通常的 spring 的配置文件没有任何区别。

timeservie-osgi.xml

<?xml version="1.0" encoding="UTF-8"?> 
<beans xmlns="http://www.springframework.org/schema/beans"   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   xmlns:osgi="http://www.springframework.org/schema/osgi"   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd   http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd"> <osgi:service id="osgiTimeService" ref="timeService" interface="org.osgichina.demo.timeservice.TimeService"> 
</osgi:service> 
</beans>

这里,我们看到了一个新的标签“”,这个标签的含义是来定义一个 osgi 的 service。这个 service 实现的接口是“org.osgichina.demo.timeservice.TimeService”,另外这个 service 引用的对象是“timeService”,也就是刚才我们看到的上一个配置文件中定义的 Spring Bean。这样的一个配置,就可以把 spring bean 发布成一个 osgi 的 service 了。是不是很简单呢。

下面我们看一下 HelloWorld 工程(见图 1-61)。

【图 1-61 HelloWorld 工程 】

可以看到,HelloWorld 文件中只有 HelloWorldImpl 这样一个 Java 文件,并且在 META-INF 下也躲了一个 spring 的目录,目录中有 helloworld-osgi.xml和 helloworld.xml 文件。我们简单看下 HelloWorld 的代码。

public class HelloWorldImpl{ private TimeService timeService; 
 
public TimeService getTimeService() { return timeService; 
}  
public void setTimeService(TimeService timeService) { this.timeService = timeService; 
}  
public void start(){ 
System.out.println("started at " +timeService.getCurrentTime()); 
}  
public void stop(){ 
System.out.println("stopped at " +  timeService.getCurrentTime()); 
} 
}

可以看到,HelloWorld 是一个简单的 bean,在调用 start 和 stop 的时候会有日志的输出。

接下来看 helloworld.xml 文件。

<?xml version="1.0" encoding="UTF-8"?> 
<beans xmlns="http://www.springframework.org/schema/beans"   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
  <bean name="hello" class="org.osgichina.demo.helloworld.HelloWorldImpl" init-method="start" destroy-method="stop" > 
  <property name="timeService" ref="osgiTimeService"/> 
  </bean>   
</beans>

可以看到,这个配置文件和普通的 spring 配置文件是一样的,定义了一个 bean,以及 init 和 destory 的方法。并且注入了一个对象给 timeService 这个属性。下面,我们来看一下 helloworld-osgi.xml。

<?xml version="1.0" encoding="UTF-8"?> 
<beans xmlns="http://www.springframework.org/schema/beans"   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   xmlns:osgi="http://www.springframework.org/schema/osgi"   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd   http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd"> 
 
<osgi:reference id="osgiTimeService" 
interface="org.osgichina.demo.timeservice.TimeService"/> 
</beans>

注意,这里也多了一个新的标签“osgi:reference”,这个是对 OSGi 服务的一个引用。这里要定义 一下接口名称,因为这是用来表示引用的服务的。

把一个 spring bean 发布为服务,以及如何引用一个 OSGi 服务作为 bean 的属性了吧。这里,可能大家有一个疑问是,如果 timeService 不是注入到 spring bean 里面,而是要在代码中获取这个服务,那该怎么办呢?很简单,我们只要通过 BundleContext 来获取这个服务就好了。服务的名字就是接口的名字,换句话说,对于这个例子中的 timeService,我们通过下面的代码就能获得这个服务:

 ServiceReference sf = context.getServiceReference(TimeService.class.getName()); 
TimeService ts = (TimeService)context.getService(sf);

到这里,例子已经完成了。在这里简单介绍一下这背后的实现。其实是 spring 的 extender 这个 bundle 帮我们完成了服务的发布和注入的工作。这个 bundle 会寻找 META-INF 下 spring 目录中的所有 xml,完成配置工作。当然,我们也可以在 MANIFEST.MF 文件中配置 spring-dm 用到的配置。而 spring 目录则是一个默认的约定。完成了这个例子,下面我们要完成一个 Spring-DM 用在 Web 场景下的例子。

1.3.4 Web版HelloWorld

前面一节已经看到了 Spring-DM 的 HelloWorld 的例子。这里,来看一下 Web 版的 HelloWorld。这里主要是想介绍在 Spring-DM 中的另外两个 Bundle,一个是 org.springframework.osgi.web,另外一个是 org.springframework.osgi.web.extender 。大家还记得,在上一节的例子中,我们用到了 org.springframework.osgi.extender 这个 Bundle。这里的org.springframework.osgi.web.extender 这个Bundle 是用在 Web 环境中的一个 Extender。

其实构造 Web 版的 HelloWorld 有多种方法。一种方式是可以在前面的 HelloWorld 的例子中通过嵌入 HttpServer 来完成一个 Web 应用。而这一节不采用这样的方案,我们采用 Spring-DM 的 WebExtender 来部署一个 Web 程序。

在 Eclipse 中有两个方案来创建我们的工程:一个是创建一个标准的 Web 工程,这样的做法有一个不方便的地方是调试的问题;另外一个办法是创建一个插件工程,而这个工程打包后是拥有 Web 应用结构的 jar 包。下面,我们就来动手完成这个例子。

1.3.4.1 环境搭建

我们在完成 Spring-DM 的 HelloWorld 的时候,搭建了一个环境。现在,要在那个环境的基础上再引入几个 Bundle ,分别是 org.springframework.osgi.catalina.osgi 、 org.springframework. osgi.catalina.start.osgi 、 org.springframework.osgi.servlet-api.osgi 、 org.springframework.osgi.web 、 org.springframework.osgi.web.extender。其中后面两个 Bundle 是从 dist 目录下导入的,前面三个是从 lib 目录下导入的。导入这些 Bundle 后,我们的 Eclipse 看起来应该是如图 1-62 所示的样子。

【图 1-62 加入新插件后的显示 】

接着,我们要修改 Run Configurations,设置 org.springframework.osgi.web.extender 的 Start Level,需要把这个 Bundle 的 Start Level 修改得比别的 Bundle 大。另外就是在 Target Platform 中要选择javax.servlet 和 org.apache.commons.logging 这两个 Bundle。如图 1-63 所示。

然后点击“Run”,可以在 Eclipse 的 Console 中看到类似如图 1-64 所示的显示。

【图 1-63 运行配置的 Bundles 】

【图 1-64 运行后的显示 】

这个时候,也可以查看到 8080 端口已经是在 LISTEN 的状态。我们的 Web 版环境准备结束,下面就来进行开发。

1.3.4.2 Web应用开发

Web 应用开发步骤如下。

>第一步,创建插件工程。

我们创建一个名为 HelloWorldWeb 的插件工程。

**> **第二步,添加 WEB-INF 目录和 index.html 文件,并且在 WEB-INF 目录中加入 web.xml 文件。 这样,我们的工程看起来应该如图 1-65 所示。

【图 1-65 HelloWorldWeb 的显示 】
**>**第三步,引入 Package(见图 1-66)。

【图 1-66 引入的 Package 】

>第四步,创建 HelloWorldServlet 。代码请见源码中 HelloWorld-SpringDM 目录下的 HelloWorldWeb 工程。并且,我们在这里也要引入前面 HelloWorld 例子中的 TimeService。

>第五步,编辑 index.html。

这里,我们可以随便在 index.html 中搞点儿什么你喜欢的。

>第六步,编辑 web.xml。

这个 web.xml 基本就是普通的 Web 应用中的配置,在这个 web.xml 中,我们加入了 Spring 的 ApplicationContext 的配置:

<context-param> 
<param-name>contextClass</param-name> 
<param-value>org.springframework.osgi.web.context.support. 
   OsgiBundleXmlWebApplicationContext</param-value> 
</context-param>

完整的内容可以参考源码中 HelloWorld-SpringDM 目录下 HelloWorldWeb 工程中的 web.xml。

>第七步,添加 applicationContext.xml。我们在 WEB-INF 目录下添加 applicationContext.xml 文件,这里的配置是为了引入 TimeService,内容如下:

<!--引入OSGi Service的定义--> 
<osgi:reference id="osgiTimeService" 
interface="org.osgichina.demo.timeservice.TimeService"/>

>第八步,配置 Run Configurations。

我们要在 Run Configurations 的 Target Platform 中加入三个 Bundle,分别是 javax.servlet.jsp、 org.apache.jasper、org.apache.commons.el。

>第九步,启动,运行。

我们会看到 Console 中会输入类似下面的信息:

    信息: Successfully deployed bundle [HelloWorldWeb Plug-in (HelloWorldWeb)] 
at [/HelloWorldWeb] on server org.apache.catalina.core.StandardService/1.0

至此,可以在浏览器上测试一下我们的应用了。

打开浏览器,输入 http://localhost:8080/HelloWorldWeb/。

会看到如图 1-67 所示的显示。

【图 1-67 浏览器结果显示 】

然后,再输入 http://localhost:8080/HelloWorldWeb/hello。

将会看到如图 1-68 所示的内容。

【图 1-68 浏览器结果显示 】

好了,我们的 Web 应用已经可以正常工作了,并且也可以方便地在 Eclipse 的集成环境中调试我 们的程序。


zhang90030 2014-10-15 21:54

第八步中,加入三个 Bundle,分别是 javax.servlet.jsp、 org.apache.jasper、org.apache.commons.el,没有说哪个版本,我用的Eclipse中没有这些bundle,从网上下载之后,运行不起来。<br>现在Spring-DM被Virgo替代了,但是看到书中PetStore的例子很吸引人,花了好多时间想把它走通,但是还是没搞定!<br>希望谁跑通了的,指导一下!!!

顶(0) 踩(0) 回复

buyaoji 2014-05-17 22:52

回复buyaoji: 找到原因了,是因为我设置了window-preference,覆盖了默认的target Platform设置。

顶(0) 踩(0) 回复

buyaoji 2014-05-17 22:12

Target Platform 中的 org.apache.commons.logging 这个 Bundle-------这步的操作是什么意思?先把这个logging bundle下载好,建到target Platform中?

顶(0) 踩(0) 回复
查看评论