我们的开发框架之所以选择使用Spring框架,是因为它提供了一个简单易用的Bean编程模型(采用IoC和AOP设计模式),通过XML配置文件简化了复杂而冗长的Bean初始化以及依赖关系的定义。不过随着Bean数量的不断增加,XML配置文件也将随之失去控制,我们很容易的就陷入了XML配置漩涡。自从Spring2.5版本推出之后,通过Annotation的方式,大大减少了XML的配置工作量。但是在Spring的OSGi版本中(Spring DM),XML的配置工作量依然可观,我们仍旧在使用<osgi:service/>和<osgi:reference/>元素来定义OSGi服务的注册和引用,由于OSGi服务的增加,我们很容易会去引用一个尚未注册的OSGi服务,一旦引用了一个未注册的OSGi服务则整个应用程序将无法正确运行。之前我还专门写过一篇文章《在Spring DM中使用Annotations发布和引用服务》,大致介绍了在Spring DM中如何使用Annotation来简化OSGi的使用,有兴趣的同学可以先去阅读一下这篇文章。接下去我们通过一个具体的实例来说明如何使用OSGi Annotation。
在Bundle中要使用OSGi Annotation,首先要定义两个BeanPostProcessor。在上一篇文章中我们已经提及过,这里我们深入的介绍它,如下图所示:
(图一)
以上两个Bean均继承至org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter,目的是为了在Spring实例化每个Bean时可以执行附加的操作,在这里我们就是要拦截初始化之后的Bean对象,检查它是否存在OSGi的Annotation,如果存在,则将此Bean注册为一个OSGi服务并登记到OSGi容器中。如果是需要引用一个OSGi服务,则会查找OSGi容器中所对应的服务,然后将其注入到当前的Bean中。其实工作原理还是比较简单易懂的。不过要记住,为了在某个Bundle中使用OSGi Annotation,必须要在当前Bundle的Spring XML中定义这两个后置处理Bean。
我们先在biz-share Bundle中编写一个接口:
package org.storevm.helloworld.biz.share; /** * * @author Administrator * @version $Id: OsgiServiceTest.java, v 0.1 2013-2-27 下午12:49:00 Administrator Exp $ */ public interface OsgiServiceTest { /** * 输出 */ void out(); }
package org.storevm.helloworld.biz.share.impl; import org.apache.log4j.Logger; import org.storevm.eosgi.core.annotation.OsgiService; import org.storevm.eosgi.core.utils.LogUtils; /** * * @author Administrator * @version $Id: OsgiServiceTestImpl.java, v 0.1 2013-2-27 下午12:53:24 Administrator Exp $ */ @Component @OsgiService(interfaces = { OsgiServiceTest.class }) public class OsgiServiceTestImpl implements OsgiServiceTest { private static final Logger LOGGER = Logger.getLogger(OsgiServiceTest.class); /** * @see org.storevm.helloworld.biz.share.OsgiServiceTest#out() */ @Override public void out() { LogUtils.info(LOGGER, "这是一个OSGi服务, name={0}", OsgiServiceTest.class); } }
@Component @OsgiService(interfaces = { OsgiServiceTest.class })
(图二)
我们可以看到,OsgiServiceTest已经被成功发布为一个OSGi服务了。不过问题还没有结束,我们只是发布了一个OSGi服务,但是其他的Bundle还不知道这个OSGi服务的存在,因此为了让其他Bundle可以使用这个服务,我们还必须将服务暴露出去,请打开biz-share Bundle中的MANIFEST.MF文件,并切换到“Runtime”选项卡。如下图所示:
(图三)
开发框架已经将所有在org.storevm.helloworld.biz.share包中的服务暴露出去了。当然,如果你需要暴露其他包中的服务,可以点击“Add”按钮,在弹出的“Exported Packages”对话框中选择需要导出的Package,然后点击“OK”按钮即可,如下图所示:
(图四)
注意:一般情况下,我们只会将接口暴露给其他Bundle引用,而不会把具体的实现类暴露出去。
接下去我们在biz-service-impl Bundle中引入biz-share Bundle发布的OSGi服务。请打开biz-service-impl Bundle中的MANIFEST.MF文件并切换到Dependencies选项卡下,如下图所示:
(图五)
从上图中我们可以发现,biz-share Bundle已经被开发框架默认配置成Required Bundle了,所以我们无需再引入该Bundle,请关闭MANIFEST.MF文件。
接下来我们在biz-service-impl Bundle中编写一个调用OSGi服务的测试类,如下所示:
package org.storevm.helloworld.biz.service.impl; import org.storevm.eosgi.core.annotation.ServiceReference; import org.storevm.helloworld.biz.share.OsgiServiceTest; /** * * @author Administrator * @version $Id: InvokeOsgiServiceTest.java, v 0.1 2013-2-27 下午1:22:45 Administrator Exp $ */ @Component public class InvokeOsgiServiceTest { /* OSGi服务 */ private OsgiServiceTest osgiServiceTest; /** * 调用服务 */ @PostConstruct public void invoke() { osgiServiceTest.out(); //调用OSGi服务 } /** * Setter method for property <tt>osgiServiceTest</tt>. * * @param osgiServiceTest value to be assigned to property osgiServiceTest */ @ServiceReference public void setOsgiServiceTest(OsgiServiceTest osgiServiceTest) { this.osgiServiceTest = osgiServiceTest; } }
@ServiceReference
通过该Annotation,当前Bean在实例化之后会去查找OSGi容器中是否有注册为osgiServiceTest的服务,如果有就会将该服务的引用对象注入到当前Bean中,如果没有就会抛出异常。
好了,让我们重新启动Eclipse IDE中的OSGi运行时容器,如果之前已经启动了,则在Console中输入close命令来关闭OSGi容器,然后再启动它。我们在启动日志中会看到如下内容:
(图七)
从上图中可以看到,我们的OSGi服务调用成功了。
在下一章中,我们将涉足分布式OSGi领域,看看开发框架为我们提供了怎么样的工具来帮助我们发布分布式的OSGi服务。