OSGi原理与最佳实践--Spring-DM简介


   作者林昊    由lan16转载自InfoQ中文站    更新于2014-09-11 20:22    已被浏览2040次

   1.简介
     Spring-DM指的是Spring Dynamic Modules。Spring-DM的主要目的是能够方便地将Spring框架和OSGi框架结合在一起,
   使得使用Spring的应用程序可以方便简单地部署在OSGi环境中,利用OSGi框架提 供 的服务,将应用变得更加模块化。
   2.环境搭建
     可以从Spring的网站上下载最新的Spring-DM包,这里提供了with-dependencies和没有dependecies两个包的下载。
   spring-osgi-1.1.2with-dependencies.zip【下载】
   spring-osgi-1.1.2.zip【下载】
     我们建议大家下载with-dependencies的这个包,这个包里面包含了Spring-DM依赖的其他的包。下载了这个Spring-DM
   包后,我们把压缩包中的dist和lib目录解压到硬盘上的某个目录,比如解压到C盘根目录下的spring-dm 目录中。
   那么我们会在C:\Spring-dm\dist目录下看到如图1-52 所示的内容。在C:\spring-dm\lib中看到如图1-53所示的内容。
     
     
     下面把spring-dm的bundle导入到Eclipse中
     我们首先导入spring-dm的包。打开Eclipse,选择Import…,在Import的对话框中选择Plug-ins and Fragments
   (见图1-54)。
     
     然后在Import Plugin-ins and Fragments对话框中做如图1-55所示的设置。
     
     我们要设置Plug-in Location,先设置为c:\spring-dm\dist,导入sprimg-dm的包。点击Next后,出现了让我们选择
   导入的Plugin界面(见图1-56)。
     
     我们可以导入core、extender、io 三个Bundle。完成后,会在Eclipse的工作区看到如图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的显示。
     
     在Run Configurations中选择目前导入的所有Bundle,然后再选择Target Platform 中的org.apache.commons.logging
   这个Bundle,运行。可以看到,所有的Bundle都是ACTIVE状态了(见图1-59)。
     
     到这里就完成了环境的搭建。下面就来做一个基于Spring-DM的应用。
   3.HelloWorld
     先看一个简单的例子。在这个例子中将展示如何把一个Spring Bean展示成服务,以及如何把OSGi的服务注入到Spring
   的Bean中。在这个例子中,有两个Bundle,分别是HelloWorld Bundle和TimeService Bundle。
     这两个工程中都没有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>”,这个标签的含义是来定义一个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-DM来把一个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目录则是一个默认的约定。