** 关于这个问题网上大多采用通过Exlipse的BND插件将JAR包OSGI化的方式解决,这里提供一种工作中实践过的另一种解决方法。
先分析一下出现这个问题的原因,该问题是由OSGI的Bundle依赖导致的,OSGI(开放服务网关)是一套以Java为技术平台的动态模块化规范。Bundle是OSGI的基本单位,它是服务(Service)和组件(Component)的载体,通俗一点理解:Bundle就是模块。在OSGI中各个Bundle之间是相对独立、对外隔离的,因此,正常情况下它们是不能互相访问内部包的,需要通过OSGI的规范来配置各个Bundle才能实现Bundle之间的访问调用。实际上当我们通过Maven向OSGI导入一个外部的JAR包时,我们导入的这个JAR包需要被OSGI化,只有这样JAR包才能被OSGI识别并管理,下面是我解决问题所使用的配置:
在pom.xml中添加如下配置:
<dependency>
<groupId>commons-beanutilsgroupId>
<artifactId>commons-beanutilsartifactId>
<version>1.9.2version>
dependency>
<build>
<plugins>
<plugin>
<groupId>org.apache.felixgroupId>
<artifactId>maven-bundle-pluginartifactId>
<configuration>
<instructions>
<Import-Package>
!org.apache.commons.collections.*,*
Import-Package>
<Embed-Directory>libEmbed-Directory>
<Embed-Dependency>
commons-beanutils
Embed-Dependency>
<Bundle-Activator>...Bundle-Activator>
instructions>
configuration>
plugin>
plugins>
build>
以Apache POI为例
1)、通过类名过滤
Export-package: org.apache.poi; include=”HSSF*”; exclude=”*impl”
上面的配置只会导出org.apache.poi路径下名称以HSSF开头的类,并且不会导出名称以impl结尾的类。
include说明需要导出Package中的哪些类,以逗号分隔,可使用通配符
exclude说明禁止导出Package中的哪些类,以逗号分隔,可使用通配符
include和exclude用于限制导出,导入时无需限制
2)、通过版本过滤
Export-package: org.apache.poi; version=”3.0” (导出时需明确指定版本号)
Import-package: org.apache.poi; version=”3.0” (导入大于3.0的版本)
Import-package: org.apache.poi; version=”[3.0, 3.15]” (可导入3.0与3.15之间的版本)
若Bundle在Export-package中没有指出版本号,则使用默认版本号0.0.0
这也就是为什么报错信息中显示版本号是0.0.0的原因了。
若想设置可导入所有版本,导入时不添加version限制即可。
3)、通过提供者过滤
OSGI允许开发者根据Bundle名称、版本等信息过滤Package,这种方式被称为“选择提供者”,这种方式很少被应用于生产环境,多用于Bundle的测试,由bundle-symbolic-name和bundle-version来实现。以下配置指明:Bundle A只会导入Bundle-SymbolicName为Bundle B、版本号在1.1.1到2.0.0之间且包路径为com.mypackage.demo的的内容(*注:此处的版本号不是Package的版本号)。
Bundle A:
Bundle-SymbolicName:BundleA
Import-package: org.apache.poi; bundle-symbolic-name=“BundleB”
bundle-version=“[1.1.1, 2.0.0)”
Bundle B:
Bundle-SymbolicName:BundleB
Emport-Package: com.mypackage.demo
Bundle-Version=“1.1.1”
4)、通过自定义属性过滤
开发人员在导入时可自定义导出属性,而在导入时就可以通过该属性进行过滤。
Export-Package: org.apache.poi; filter:=”true”
对于以上导出内容,下边的导入只有Bundle A和Bundle B能够成功导入:
Bundle A:
Import-Package: org.apache.poi(成功导入,原因下边说明)
Bundle B:
Import-Package: org.apache.poi; filter:=”true”(成功导入)
Bundle C:
Import-Package: org.apache.poi; filter:=”false”(导入失败,没有匹配的Bundle)
Bundle A之所以能够导入是因为filter属性并没有被声明为必须的。如果需要将自定义属性声明为必须匹配项,则需要使用mandatory属性。
如果将导出改为:
Export-Package: org.apache.poi; filter:=”true”; mandatory:=”filter”,
则只有Bundle B能够成功导入org.apache.poi包。mandatory的缺省值是optional, 即可选的,因此在没有指定mandatory:=”filter”时,Bundle A能够成功导入。
5)、可选导入
如果导入某个Package是为了实现一些不影响Bundle正常运行的附加功能,比如系统的国际化,不导入国际化对应的Bundle也不会影响系统的运行,只是系统只能以默认语言展示。此时可以使用resolution来指定某一个依赖是否为必须的。
Bundle A:
Import-Package: org.apache.poi; resolution:=”mandatory”
Bundle B:
Import-Package: org.apache.poi
Bundle C:
Import-Package: org.apache.poi; resolution:=”optional”
以上配置中的Bundle A和Bundle B时等价的,因为resolution的默认值是mandatory, 对于Bundle A和Bundle B,如果没有任何Bundle导出org.apache.poi包,则会解析失败,而Bundle C则不会,因为他是可选的,如果找不到指定Package则会跳过。
6)、动态导入
存在这样一种应用场景:某个导入包Paceage时必需的,但是提供Package的Bundle并不会在系统启动时被安装,在这种情况下,我们需要在使用时再去导入这个Package,DynamicImport-Package能够满足这样的需求。
Bundle A:
Import-Package: org.apache.poi
Bundle B:
DynamicImport-Package: org.apache.poi
如果没有任何Bundle导出org.apache.poi包,Bundle A会解析失败,而Bundle不会被影响,但是如果Bundle B在使用到org.apache.poi时,仍然没有任何Bundle导出org.apache.poi包,则会抛出ClassNotFoundException异常。与Import-Package不同的是DynamicImport-Package可以使用通配符,
比如:DynamicImport-Package: org.apache.*
7)、导出Package的限制
如果存在多层依赖的情况,则需要在导出时使用uses属性进行说明,比如:
Export-Package: org.apache.poi; uses:=”javax.servlet.http”
Import-Package: org.apache.poi
以上配置说明:在导入org.apache.poi包时,会递归导入javax.servlet.http包,因为
org.apache.poi包的正常使用依赖于javax.servlet.http包
8)、导入整个Bundle
OSGI支持Bundle级别的导入,可以使用Require-Bundle来声明需要导入的Bundle,这个标志将会导入指定Bundle内所有声明为导出的Package。
Require-Bundle: Bundle A(Require-Bundle后需指明要导入的Bundle名称)