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-dmdist 目录下看到如图 1-52 所示的内容。在C:spring-dmlib 中看到如图 1-53 所示的内容。下面把 spring-dm 的 bundle 导入到 Eclipse 中。
我们首先导入 spring-dm 的包。打开 Eclipse,选择 Import…,在 Import 的对话框中选择 Plug-ins and Fragments(见图 1-54)。我们直接在 Run Configurations 中选择这三个 Bundle,并执行,会发现三个 Bundle 都是 INSTALLED状态,如果我们启动Bundle,会报错,原因是我们没有加入这三个Bundle所依赖的Bundle。而这些Bundle,也就在 c:spring-dmlib目录下。我们用和前面一样的方式来导入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 的显示。在 Run Configurations 中选择目前导入的所有 Bundle,然后再选择 Target Platform 中的 org.apache.commons.logging 这个 Bundle,运行。可以看到,所有的 Bundle 都是 ACTIVE 状态了(见图 1-59)。
到这里就完成了环境的搭建。下面就来做一个基于 Spring-DM 的应用。1.3.3 HelloWorld
先看一个简单的例子。在这个例子中将展示如何把一个Spring Bean展示成服务,以及如何把OSGi 的服务注入到 Spring 的 Bean 中。在这个例子中,有两个 Bundle,分别是 HelloWorld Bundle 和 TimeService Bundle。例子代码见源码 Spring-DM 中的 HelloWorld 工程和 TimeService 工程。
这两个工程中都没有 BundleActivator。在 Bundle 启动和停止的时候不用去做什么事情。先来看 一下 TimeService 工程(见图 1-60)。可以看到,我们在 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)。可以看到,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 所示的样子。接着,我们要修改 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 所示的显示。这个时候,也可以查看到 8080 端口已经是在 LISTEN 的状态。我们的 Web 版环境准备结束,下面就来进行开发。
1.3.4.2 Web应用开发
Web 应用开发步骤如下。
>第一步,创建插件工程。
我们创建一个名为 HelloWorldWeb 的插件工程。
**> **第二步,添加 WEB-INF 目录和 index.html 文件,并且在 WEB-INF 目录中加入 web.xml 文件。 这样,我们的工程看起来应该如图 1-65 所示。>第四步,创建 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 所示的显示。然后,再输入 http://localhost:8080/HelloWorldWeb/hello。
将会看到如图 1-68 所示的内容。好了,我们的 Web 应用已经可以正常工作了,并且也可以方便地在 Eclipse 的集成环境中调试我 们的程序。