Spring-DM筆記之osgi:reference

現下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
發佈了58 篇原創文章 · 獲贊 0 · 訪問量 3369
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章