Hello Spring OSGi
2
Nov
Nov
13
源起
Spring 有廣大的用戶與部署經驗,當然也要試看看,包起來如何運作。
環境部署
因為目前文件尚未完備,bundle 之間相依性很複雜,這裡直接用 simple-service-bundle 來測試,順便看看要載入多少東西。所有的 bundle 除了 OSGi 之外,全部取自下面連結。
http://static.springframework.org/maven2-snapshots/
如何取得服務
這個 bundle 會做兩種存取 simple-service-bundle 的方式的測試,差別在生命週期掌握在誰的手上。
- Declarative Services (OSGi SCR)
- osgi:reference (Spring Ioc)
關於這兩種做法,摘錄 Spring OSGi Specification (v0.7) 的說法如下。
http://www.springframework.org/osgi/specification
Spring-OSGi enabled bundles can co-exist happily with bundles using DS, but for new bundle development we recommend the use of the Spring OSGi support in place of DS.
下面是設定檔的部份。
META-INF/MANIFEST.MF
Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: haha.foo Bundle-SymbolicName: haha.foo Bundle-Version: 1.0.0 Bundle-Localization: plugin Import-Package: org.osgi.service.component;version="1.0.0", org.osgi.service.log;version="1.3.0", org.springframework.osgi.samples.simpleservice Service-Component: OSGI-INF/reference.xml Require-Bundle: org.springframework.osgi
OSGI-INF/reference.xml
<?xml version="1.0" encoding="UTF-8"?>
<component name="haha.foo.Component">
<implementation class="haha.foo.Component"/>
<reference name="LOG" interface="org.osgi.service.log.LogService"
cardinality="1..1" bind="setLog" unbind="unsetLog" policy="static"/>
<reference name="MYSERVICE"
interface="org.springframework.osgi.samples.simpleservice.MyService"
cardinality="1..1" bind="setMyService" unbind="unsetMyService"
policy="static"/>
</component>
META-INF/spring/bean.xml
<osgi:reference id="myService"
interface="org.springframework.osgi.samples.simpleservice.MyService"/>
<bean name="foobean" class="haha.foo.Bean">
<property name="myService" ref="myService"/>
</bean>
結果
osgi> ss Framework is launched. id State Bundle 0 ACTIVE system.bundle_3.3.0.v20061023 4 ACTIVE org.eclipse.equinox.ds_1.0.0.v20060828 5 ACTIVE org.eclipse.equinox.log_1.0.100.v20060717 6 ACTIVE org.eclipse.osgi.services_3.1.100.v20060918 8 ACTIVE org.apache.commons.logging_1.0.4 10 ACTIVE log4j_1.2.13 11 ACTIVE org.aopalliance_1.0.0 12 ACTIVE org.springframework.aop_2.0.0 13 ACTIVE org.springframework.beans_2.0.0 14 ACTIVE org.springframework.context_2.0.0 15 ACTIVE org.springframework.core_2.1.0 16 ACTIVE org.springframework.osgi_2.0.0 20 ACTIVE haha.foo_1.0.0 21 ACTIVE org.springframework.osgi.samples.simpleservice_1.0.0
觀察
- 覺得由 spring 來存取比目前 ds 方便,spring-osgi 讓你兩種方法都可以用,很有彈性,這也代表很容易嵌入現有的 OSGi (例如 RCP) 架構中使用。
你在javaword发表的haha.foo那个测试我已经拜读过了,非常感谢。另外想向你请教一个困扰多时的问题。就是spring-osgi中,applicaion context是在bundle启动的时候自动装载的,那么我如何获取在spring/bean.xml中定义的bean的实例呢,能给出一些指导语句吗?谢谢。 就是如你在bean.xml中定义的 。。。 那么在bundle内如果类似ctx.getBean("foobean")来获取这个bean 我的email:lionger@163.com,你可回复我邮箱,多谢
就我目前了解,需要將這個 bean 變成 OSGi 的服務並發布,利用 spring-osgi 的 osgi:service 可以做到。這樣你就可以在別的 bundle 中用 osgi:reference 取出,而不需要找到 application context。
关于bundle间的bean的注入确实是这样的。spring-osgi-core统一管理所有bundle中的application context,所以可以自动建立bundle间的这种osgi:reference 。而一个bundle内部应该也有从application context获取自己配置的bean实例的需要吧?既然ctx是会由spring-osgi统一自动创建的,我想应该有某个方法可以获取到吧。在其他bundle获取另一个bundle的ctx,可以利用类如bundle-id-springApplicationContext的名称获取服务,那获取自己bundle内的是否也可以使用这个方法呢?我会去实验一下,希望有知道的人也可以指导。多谢
启动osgi后,运行 osgi bundle 2148 initial@reference:file:F:/Java/OSGI/haha.foo-2006-1103/haha.foo/ [2148] Id=2148, Status=ACTIVE Data Root=E:\Workspace\spring-osgi\.metadata\.plugins\org.eclipse.pde.core\Simple Service\org.eclipse.osgi\bundles\2148\data No registered services. Services in use: 发现竟然没有注册服务,同样我运行 osgi bundle 2362 initial@reference:file:E:/Workspace/spring-osgi/spring-osgi/samples/simple-service/simple-service-bundle/ [2362] Id=2362, Status=ACTIVE Data Root=E:\Workspace\spring-osgi\.metadata\.plugins\org.eclipse.pde.core\Simple Service\org.eclipse.osgi\bundles\2362\data Registered Services {org.springframework.context.ApplicationContext}={org-springframework-context-service-name=org.springframework.osgi.samples.simpleservice-springApplicationContext, service.id=36} 发现注册了ApplicationContext服务,却没有发现在spring/simpleervice-osgi.xml中定义的服务。能帮我想一下是为什么吗?thx
實際情形不清楚,不過之前在 eclipse 上用,有時候會出現註冊不到的問題,起因要註冊的的服務的 bundle 比 spring-osgi bundle 先啟動完成,於是沒被 spring-osgi 動到,這點還有待研究。修正方式去調 start-level 或是改 import package 為 require package 或是整個 equniox 的執行刪掉,再重建一個新的,總之目前這個應用還不成熟,只能多試試。
至於你談到關於自己 bundle 中 ctx.getBean 的事情,現在都直接注入到 bean 來取用,沒有直接 getBean 抓出來,似乎在 unit test 才會動到,有機會再試看看。
你好,lin,又来向你请教问题了:假设有一个类BeanService,没有实现任何接口。利用spring-osgi发布为一个服务 这样是没有问题的。可是我在另外的bundle引用这个服务时抛错了。 错误如下: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'beanService': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: com.BeanService is not an interface Caused by: java.lang.IllegalArgumentException: com.BeanService is not an interface 错误的产生是在at org.springframework.osgi.service.OsgiServiceProxyFactoryBean.constructProxy(OsgiServiceProxyFactoryBean.java:242)。好像是使用什么动态代理。 如果是这样的话,如果想把一些没有实现接口的类,发布为service来提供服务,就无法利用spring-osgi了,则能使用DS中的定义方法了?
注册服务:osgi:service id="beanServiceOSGI" ref="beanService" interface="com.BeanService" 、 应用服务 osgi:reference id="beanService" interface="com.BeanService"/
確實是 DS 可以支援 class 直接當服務,不過規格也不建議這樣做, 參考 OSGi 規格 112 Declarative Services Specification 的 112.4.6 Service Element 部份。關於 XML 設定中 service/provide[@interface] 是可以寫 class 名字,但是也提到應該用 interface 為佳。
這種規格書可能 會讓部份支援規格的 OSGi DS 平台,只考慮做到 interface 支援,建議還是 儘可能擠出個 interface 來用比較可靠。
interface – The name of the interface that this service is registered under. This name must be the fully qualified name of a Java class. For example, org.osgi.service.log.LogService. The specified Java class should be an interface rather than a class, however specifying a class is supported.
多谢Lin的讲解。我原来的打算是在一个bundle里面创建一个org.springframework.orm.hibernate3.LocalSessionFactoryBean并发布为服务,这样,其他bundle需要使用spring-hibernate中的功能的时候,直接使用这个sessionFactory服务。不过既然无法将未实现接口的类直接申明为服务,所以我就把dataSource发布为服务,其他模块需要使用到spring-hibernate功能,则ref这个dataSource,创建一个新的sessionFactory。 ?bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"? ?property name="dataSource"?ref bean="dataSource"/??/property? 说得比较乱,希望你能明白我的意思。请问你有没有其他方法来实现这种意图?
另外请教另一个问题。就是利用applicationContext.xml文件定义bean的时候,经常会出现NoClassDefFoundError异常。比如我上面写的声明sessionFactory,就会抛出java.lang.NoClassDefFoundError: org.hibernate.cfg.Configuration,但我在bundle中的代码里,是不会抛出这样的异常的。 无论我是import进来org.hibernate.cfg,还是利用classpath定义hibernate3.jar都会出现这种问题。是不是spring在创建bean的时候,classloader和bundle的classloader不一样啊?
不確定問題是否一樣,不過之前練習 hibernate bundle 需要加上 Eclipse-BuddyPolicy: registered, 要用的那一端需要 Eclipse-RegisterBuddy: haha.remix.hibernate 之類的,才能讓 hibernate 找到。
http://blog.extremepattern.com/articles/2006/11/07/hello-spring-jpa-and-osgi
還沒有練習到多個 bundle 共用一個 LocalSessionFactoryBean 的做法,有機會再試試。
暫時想到三種做法,一是轉包 extends LocalSessionFactoryBean implements MyOrmInterface, 用 osgi:reference interface=MyOrmInterfce 來做。
第二種是用 org.springframework.beans.factory.FactoryBean 來當 interface,不過擔心會找到 一堆這種服務,不見得是 LocalSessionFactoryBean,除非只有一個這種服務。
第三種是 Spring OSGi Specification 中提到卻沒有說明的 osgi:reference[@filter], 查了一下 org.springframework.osgi.config.ReferenceBeanDefinitionParser 程式碼, 其中還沒有處理 filter 的部份,所以這種做法可能還要等。