现下osgi可谓是一个比较有人气的方向,它为应用系统带来了强大的动态解决方案。在某些程度上osgi同Maven、Antx十分相似,它们都提供了版本化条件化的引用系统,让我们在编译期、运行期拥有更加大的灵活性。同样的思想也可以运用在IOC的实现上,现在主流的IOC一般以名称、类型作为引用依赖定位的依据,并且带有强烈的一对一基数限制,spring-dm的出现将osgi强大的动态能力很自然地集成到IOC容器中,使之易于使用又具备很高的灵活性。
在整合spring和osgi的过程中,要做的很重要的一件事情就是如何将osgi的service引入到spring的管理体系中来。在spring一般依据属性(字段)的名字来确定这个属性的(字段)运行期注入物,但是在osgi的service体系下,光靠简单的名称或者类型来过滤符合条件的service,可能会得到好几个候选对象,这同spring的依赖解析是有矛盾的。再加上osgi的动态性,service的生命周期比较复杂,符合条件的service可能随时会被卸载,当然也可能有新的符合条件的service不知道什么时候就出现了。
在spring-dm中,OsgiNamespaceHandler负责解释所有同osgi相关的xml片段。用于解释导入osgi服务相关的主要有以下代码:
registerBeanDefinitionParser(”reference“, new ReferenceBeanDefinitionParser());
registerBeanDefinitionParser(”list“, new CollectionBeanDefinitionParser() {
protected CollectionType collectionType() {
return CollectionType.LIST;
}
});
registerBeanDefinitionParser(”set“, new CollectionBeanDefinitionParser() {
protected CollectionType collectionType() {
return CollectionType.SET;
}
});
这里主要看一下关于reference的内容,list、set基本的思路与reference基本一致,其实就是reference的复数(集合)形式。
首先可以看到负责解释reference的是ReferenceBeanDefinitionParser,这个类的精简之后的代码如下:
class ReferenceBeanDefinitionParser extends AbstractReferenceDefinitionParser {
protected Class getBeanClass(Element element) {
return OsgiServiceProxyFactoryBean.class;
}
}
从这里基本可以看出,一个reference元素被解析之后,将产生一个关于OsgiServiceProxyFactoryBean的BeanDefinition,xml元素携带的信息由这个FactoryBean接受并负责在运行创建合适的service对象代理。下面来看看这个FactoryBean中最主要的一个方法的代码:
Object createProxy() {
// 关于调用时线程上下文ClassLoader的拦截器。
// osgi规范没有定义在导出的service被调用时的线程上下文ClassLoader,spring-dm通过环绕拦截器的方式增加了这一控制点。在xml配置中可以指定unmanaged、service-provider。
final ServiceProviderTCCLInterceptor tcclAdvice = new ServiceProviderTCCLInterceptor();
// 监听器,实时获得当前被选中的service,从而实时更新当前的service-provider,设置正确的ClassLoader。说白了就是提供正确的ClassLoader给上面的拦截器。
final OsgiServiceLifecycleListener tcclListener = tcclAdvice.new ServiceProviderTCCLListener();
// 从变量的命名可以看出lookupAdvice,非常可能是在确定当前符合各项过滤限制的service对象,
实际上确实是这样,但是由于osgi的动态性,对于service的管理还是有一定复杂度的。
final ServiceDynamicInterceptor lookupAdvice = new ServiceDynamicInterceptor(getBundleContext(),
getUnifiedFilter(), getAopClassLoader());
// 设置service的依赖策略
lookupAdvice.setRequiredAtStartup(getCardinality().isMandatory());
// 这里可以看到在lookupAdvice被赋予了两部分的监听器。第一部分是getListeners()提供的,这一部分是由reference的子元素listener定义的。第二部分是tcclListener,这一个就是刚刚看到的那个ClassLoader的监听器了。
OsgiServiceLifecycleListener[] listeners = addListener(getListeners(), tcclListener);
lookupAdvice.setListeners(listeners);
synchronized (monitor) {
lookupAdvice.setRetryParams(retriesNumber, retryTimeout);
retryTemplate = lookupAdvice.getRetryTemplate();
}
lookupAdvice.setApplicationEventPublisher(applicationEventPublisher);
lookupAdvice.setStateListeners(stateListeners);
lookupAdvice.setServiceImporter(this);
// 定制一个ServiceProxyCreator,这里主要是定制createDispatcherInterceptor,createServiceProviderTCCLAdvice
ServiceProxyCreator creator = new AbstractServiceProxyCreator(getInterfaces(), getAopClassLoader(),
getBeanClassLoader(), getBundleContext(), getContextClassLoader()) {
ServiceInvoker createDispatcherInterceptor(ServiceReference reference) {
// 由lookupAdvice充当ServiceInvoker
return lookupAdvice;
}
Advice createServiceProviderTCCLAdvice(ServiceReference reference) {
return tcclAdvice;
}
};
// 通过ServiceProxyCreator创建一个service的代理对象,处理对于这个service的方法调用。
ProxyPlusCallback proxyPlusCallback = creator.createServiceProxy(lookupAdvice.getServiceReference());
synchronized (monitor) {
proxy = proxyPlusCallback.proxy;
destructionCallback = new DisposableBeanRunnableAdapter(proxyPlusCallback.destructionCallback);
}
lookupAdvice.setProxy(proxy);
lookupAdvice.afterPropertiesSet();
return proxy;
}
createServiceProxy方法大致的思路就是利用spring-aop的ProxyFactory结合一系列相关的advice创建出合适的proxy。到这儿,粗略的处理过程就是这样,但是好像几乎没有看到同osgi相关的什么东西,这里我们忽略了一个十分重要的对象ServiceDynamicInterceptor,正是这个拦截器它处理了service的定位。
ServiceDynamicInterceptor继承了ServiceInvoker,先看看ServiceInvoker的代码:
protected Object doInvoke(Object service, MethodInvocation invocation) throws Throwable {
Method method = invocation.getMethod();
try {
return method.invoke(service, invocation.getArguments());
}
catch (IllegalAccessException ex) {
throw (RuntimeException) new IllegalAccessException(….)
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
public final Object invoke(MethodInvocation invocation) throws Throwable {
return doInvoke(getTarget(), invocation);
}
不难得到getTarget这个方法的实现应该是这个体系中关键的一环,再继续查看在ServiceDynamicInterceptor中getTarget的实现,可以看到它是依赖于private ServiceWrapper wrapper这样一个字段的。在代码中查找对wrapper的操作,很多重要的操作都是由一个名为Listener的ServiceDynamicInterceptor的内部类发起的,而这个内部类唯一的实例在afterPropertiesSet方法中被引用了,OsgiListenerUtils.addSingleServiceListener(bundleContext, listener, filter)。至此终于看到同osgi关联比较紧密的地方了,这是一个监听osgi中关于service状态变更的重要入口。Listener中的serviceChanged方法主要是针对service的注册、修改、卸载做了相应的回调处理,并依据当前已经存在的ServiceReference做必要的更新。当然在卸载的时候会重新启动原始的基于osgi的service查找,newReference = OsgiServiceReferenceUtils.getServiceReference(bundleContext, (filter == null ? null : filter.toString())),其中spring-dm会依据ranking(越大越好)、serviceId(越小越好)的原则过滤出至多一个ServiceReference,然后模拟service修改的方式再次调用serviceChanged方法来处理剩下的问题。最后在适当的地方播发service的bind、unbind事件(tcclListener 等)。如果在调用getTarget方法时不能确定service引用,则表示当前没有任何时候的对象被发布到osgi的运行环境中,这个时候会触发ServiceUnavailableException的抛出。
总的来看,spring-dm对osgi中service的引入是基于aop技术的,通过一个代理对象向外提供一个稳定的统一的引用(占位符),同时通过不同的advice实现它不同的附加功能,最重要的lookupAdvice使用osgi框架的ServiceListener监听相关service的动态更动,从而实时更新目标service(其实是对wrapper对象的实时维护)。从动态性上面来看lookupAdvice有点类似于spring-aop中的TargetSource。
PS:以上代码阅读基于spring-osgi-1.1.2