在使用Spring的时候,我们习惯于用bean的名称作为注册、查找的条件,这也就意味着bean的引用是唯一的了,而不能来查找、注入一系列具备相同功能但不同实现的bean,这种应用的场景其实还是很多的,尤其在扩展的场景中,在这篇blog中以一个应用场景来说明下这种需求,顺便也宣传下OSGi的服务接口+版本+属性的注册和查找机制。
以将Spring bean发布为DSF服务的bean来讲,这个bean需要做到根据发布DSF服务的方式,调用相应的具体发布DSF服务的实现bean,同时要做到的自然是在以后增加了新的发布方式后,不需要修改这个bean的代码。
要实现这个需求,首先想到的是这样的解决方案:
1、在这个bean中直接注入所有的发布DSF服务实现的bean,在调用的时候可以根据规则寻找到相应的bean,这个在Spring中能够实现的方式也许是这样:
<bean id="DSFServiceExporterFactory" class="将Spring bean发布为DSF服务的bean">
<property name="exporterClassMap">
<map>
<entry key="jndi"><ref bean="以JNDI方式发布DSF服务端的bean"></entry>
<entry key="webservice"><ref bean="以Webservice方式发布DSF服务端的bean"></entry>
</map>
</property>
</bean>
这是一种实现方式了,这样当以后增加了新的发布方式后,通过增加bean的定义以及修改这里map里面的东西就可以了。
2、第二种实现方法就很常见了,就是写个properties文件,配置各种发布方式具体对应的实现类。
但这两种方法都不够的优雅,它们都有个共同的特点,就是需要去维护一个共同的配置的地方,想象中最好的解决方法是类似这样的配置:
<bean id="DSFServiceExporterFactory" class="将Spring bean发布为DSF服务的bean">
<property name="exporterClasses">
<ref bean="cn.bluedavy.dsf.exporter.*">
</property>
</bean>
这样就把所有的cn.bluedavy.dsf.exporter.开头的bean都注入到exporterClasses里了,当然,在根据具体的协议查找实现类时,又得提供一种支持方法了,好,不再去自己瞎琢磨了,来看看如果是OSGi的服务模型的话,会怎么样去实现这样的东西:
在OSGi中每个对外提供的service都以接口来定义,在上面的场景中,很明显,自然会出现的一个现象就是如果多个服务实现同样的接口的话怎么去找到自己要的那个服务呢,OSGi中多数采用的方法是为这个服务加上一个属性标识,在查找服务时就可以通过接口,再加上属性标识来查找到想要的服务了,又或者可以只查找接口,获取到所有实现这类接口的服务。
按照这样的模型的话上面的场景就很容易实现了,写个模拟的配置文件:
<osgi:reference id="exporterClasses" interface="cn.bluedavy.dsf.exporter.DSFExporter"/>
<bean id="DSFServiceExporterFactory" class="将Spring bean发布为DSF服务的bean">
<property name="exporterClasses">
<ref bean="exporterClasses">
</property>
</bean>
这个和真实的会有些不同,但意思是差不多的,:),而OSGi的服务呢,在注入后可以直接通过ServiceReference这个对象来获取到这个服务的相关属性信息,这样其实也就可以做到类似之前第一个解决方案的效果,但同时又解决了不需要维护统一文件的问题。
服务模型的这种语义性质的机制使得服务的概念表达得更为清楚,使用得也更为方便了,以接口来表达服务的功能是一种非常合理的方式,同时辅以属性来描述实现此功能的方式(有点像元数据里的keyword)无疑更加全面的表达了此服务, 也使得使用者能够更合理的选择需要的服务实现。
版本机制在这篇blog中没有提及,这个我想在实际的过程中大家应该都深受版本混淆的痛苦了,:),在这里不多描述了,服务模型目的无疑就是为了更加清晰的表达一个功能,并且让使用者更加方便的查找、使用所需要的功能。