9.5. 导出者/导入这监听器最佳实践
如前所述,Gemini Blueprint/Spring DM导出者与导入者允许用监听器接收服务绑定、解绑、注册或者注销的通知。当使用监听器时,有一些指导建议:
- 在监听器中不要执行长耗时任务。如果必须执行耗时活动任务,需要用单独的线程来执行这个工作。监听器是同步调用的,所以越快越好。在监听器的操作会阻止发送其它的事件,OSGi服务会将活动挂起。
- 尽可能使用listener的自定义声明-不要将你的代码绑定到Gemini Blueprint/Spring DM API,不要强制使用特定的签名。
- 如果你发现在listener定义中重复声明bind/unbind方法,考虑使用Spring的bean定义继承来定义公共定义,公共定义可以服用并按需自定义。
- 偏好java.util.Map,而不是java.util.Dictionary。Map是接口,而Dictionary是被废弃了的抽象类。为了保留兼容性,Gemini Blueprint/Spring DM会将传递给监听器的Map实现强制转换为Dictionary(如果需要的话)。
- 使用重载方法时要小心:所有匹配特定服务类型的方法都会被调用,这可能不是希望的。考虑下面的监听器:
public class MyListener {
void register(1 Object service, Map properties);
void register(2 Collection dataService, Map properties);
void register(3 SortedSet orderedDataService , Map properties);
}
1、Object类型-匹配所有的服务。这个方法永远会被调用。
2、Collection类型-如果调用这个方法,那么Object方法也会被调用。
3、SortedSet类型-如果调用这个方法,那么Object方法和Collection方法都被调用。
9.5.1. 监听器和循环依赖
有一些场景导出者和导入者需要引用定义的bean:
<bean id="listener" class="cycle.Listener"> 1
<property name="target" ref="importer" /> 2
</bean>
<osgi:reference id="importer" interface="SomeService"> 3
<osgi:listener bind-method="bind" ref="listener" /> 4
</osgi:reference>
1、监听器bean
2、依赖:监听器 -> 导入者
3、导入者声明
4、依赖: 导入这 -> 监听器
上面的声明虽然合法,但是它让监听器和导入者互相依赖。为了创建导入者,就必须解析监听器,为了创建监听器,又必须获取(实例化和配置)导入者服务。需要破解这个循环,才能完全创建和配置bean。对于导出者和导入者来说,Gemini Blueprint/Spring DM都支持这种场景,。然而,如果监听器定义为内联的bean,循环将无法解析:
<osgi:reference id="importer" interface="SomeService">
<osgi:listener bind-method="bind">
<bean class="cycle.Listener">
<property name="target" ref="importer" />
</bean>
</osgi:listener>
</osgi:reference>
1、OSGi服务导入者
2、依赖:导入者 -> 监听器
3、内嵌的监听器声明
4、依赖:内嵌的监听器 -> 导入者
bean和循环
循环依赖(A依赖于B,同时B依赖于A)增加了配置的复杂性,在大多数情况下,这都是设计问题。什么样的bean可以被创建和配置?虽然不是好的实践,但是当涉及单例时(因为实例可以被缓存起来),Spring容器仍努力尝试解析循环配置。然而这不并总是有效的,严重依赖特定的配置(bean类可以部分实例化吗?它依赖于特殊的接口吗?涉及到BeanPostProcessors吗?)
上面的例子将会失败,因为服务bean不能够实例化,它依赖于监听器。之前看到过同样的循环,但是这种情况有点微妙且从容器的角度来看,有很大不同-声明的监听器为内嵌的或者内部的bean(因此没有bean id)。内部的bean与它们的父bean具有相同的生命周期。从定义上看,它们没有被容器跟踪,只是简单的按需创建。由于导入者不能部分创建,内嵌的监听器也不能缓存,容器就不会拆解循环,也不创建bean。虽然上面的两个配置是类似的,但是一个能工作,另一个却不能工作。这是另一个不要使用循环的原因,如非你真的必须用。
总之,如果你需要在监听器内持有导出者或者导入者的引用,要么将监听器声明为顶级的bean,要么考虑依赖查找。然而,后者需要额外的上下文信息,例如使用的BeanFactory,bean名字等,并且更加脆弱。
注意
给那些想对技术细节感兴趣的人。不管导出者还是导入者都不能部分实例化,因为它们依赖于应用上下文类加载器,而上下文类加载器是通过BeanClassLoaderAware访问的,BeanClassLoaderAware又依赖于内置的BeanPostProcessor,显然BeanPostProcessor只有在bean配置和实例化后才能应用。如果不需要ClassLoader,那么导出者或者导入者可以部分实例化,就支持上面的场景了。
9.6. 服务导入者的全局默认值
对于文件中声明的所有导入者,osgi命名空间提供了两个全局属性用于指定默认的行为。这样,当我们使用osgi命名空间来包装set、list和reference元素,你可以使用下面的属性:
- default-timeout:对于那些没有显示指定超时时间的导入者元素,可以用该属性指定的超时时间(单位毫秒)。例如:
<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" 1
osgi:default-timeout="5000"> 2
<reference id="someService" interface="com.xyz.AService"/>
<reference id="someOtherService" interface="com.xyz.BService" 3
timeout="1000"/> 4
</beans:beans>
1、声明osgi命名空间前缀。
2、声明根元素的default-timeout(单位毫秒),如果默认值没有设置,它的值就为5分钟。本示例中就是5秒。
3、这个引用继承默认的超时时间,因为它没有显示指定,这个服务引用的超时时间为5秒。
4、这个引用声明了超时时间,覆盖了默认值。这个服务引用的超时时间为1秒。
- default-availability:对于那些没有显示指定可用性的导入者元素,可以用该属性指定默认的可用性。可能的值为optional和mandatory。Spring DM 1.x中使用的default-cardinality属性仍然可用,但是已经被废弃。考虑下面的例子:
<beans:beans
xmlns="http://www.springframework.org/schema/osgi" 1
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans" 2
xmlns:osgi="http://www.springframework.org/schema/osgi" 3
osgi:default-availability="optional" 4
default-lazy-init="false"> 5
<reference id="someService" interface="com.xyz.AService"/> 6
<set id="someSetOfService" interface="com.xyz.BService"/> 7
<list id="anotherListOfServices" interface="com.xyz.CService"
availability="mandatory"/> 8
</beans:beans>
2、导入Spring框架的beans shema,命名空间关联为beans。
3、导入Gemini Blueprint schema,命名空间关联为osgi。由于需要为beans元素声明全部属性,为了避免模糊,Gemini Blueprint/Spring DM schema也指定一个前缀。
4、为根元素声明default-availability。如果没有设置,默认值为mandatory。本例中默认值为optional。注意osgi为添加到全局属性了。
5、beans元素属性不需要前缀(例如default-lazy-init),因为它们被声明为本地的,没有限定。
6、reference声明继承默认的availability值,因为它没有指定。
7、set声明继承默认的availability值,因为它没有指定。
8、list声明指定了availability值(mandatory),覆盖了默认值。