本文是纯第三方依赖的Eclipse插件的构建和自动更新的后续。
背景
之前构建纯第三方依赖的Eclipse插件的时候,通过Export-Package
导出了不少包(例如apache commons、xstream、slf4j等)给其他插件使用,因为导出包太多,导致Eclipse插件在查找类的时候出现了性能问题,所以现在想仅开放必要的内部包,公共组件的包由外部插件提供,一些必要的调用通过该插件内部的代码类来提供。所以做了两个修改:
- 从
Export-Package
中移除了公共组件的包,仅保留项目相关jar的包的导出。 - 代码工程中增加了src/main/java源码文件夹,通过自定义工具类实现对插件内部类的配置。
经过修改后重新打包,发现打包后的bundle jar中除了自定义的类、第三方jar之外,Export-Package
中定义的导出包及其中的类也被解压到了bundle jar中,解压后的包和class以及jar都存在于bundle jar中,这样肯定是有问题的。
问题原因
通过查阅BND的资料,发现之前对Export-Package
的理解有误,之前一直以为该配置对应的是Bundle MANIFEST.MF中的Export-Package,添加到该配置中的插件包及插件内部jar中的包可以供外部插件访问。其实BND中的Export-Package
并不仅仅是这个功能,先贴一段BND中的解释说明:
<Export-Package>
The <Export-Package> instruction is a list of packages for the bundle to export. These packages are copied into the resulting bundle JAR file from the available classes (i.e., project classes, dependencies, and class path); thus, it is possible to include classes into your bundle that are not associated with source files in your project. <Export-Package> can be specified with package patterns using the '*' wildcard. Also, it is possible to exclude packages using negation by starting the package pattern with '!'. Thus, non-negated patterns indicate which of the available packages to include in the bundle, whereas negated patterns indicate which should not be included in the bundle.The list of package patterns is ordered and earlier patterns are applied before later patterns. For example, if you specify "org.foo.,!org.foo.impl" the second pattern has no effect since all org.foo packages have already been selected by the first pattern. Instead, you should specify "!org.foo.impl,org.foo.", which will export all org.foo packages except org.foo.impl.
Following standard OSGi R4 syntax, package patterns can include both directives and attributes, which will be copied appropriately into the generated Export-Package manifest header. Besides explicitly listing package version attributes, BND will also determine package versions by examining the source JAR file or from packageinfo files in the package directory.
注意上述说明中的黑体部分。重点在第一段,第一段已经说明了添加到Export-Package中的包中的class会被复制到生成的bundle jar中,同时加入到MANIFEST.MF的Export-Package头中。
但是我们之前构建的bundle jar,并没有将Export-Package包中的class复制到bundle jar中,是本次增加了src/main/java后才出现解压class的现象,原因未知。
解决办法
BND官网的常见问题文档中,第一条即是该问题“当我嵌入bundle中一个依赖时为什么我在bundle jar中看见了重复的类?”,看来这个问题比较普遍_。先来看BND给的解释说明:
Having two copies of classes, both unpacked and embedded as jars, is a sign that your Embed-Dependency and Export-Package instructions are overlapping. Export-Package tells BND to pull in classes found in the named packages from the build classpath and add them to the bundle, Embed-Dependency tells BND to embed (or inline if enabled) whole artifacts in the bundle.
BND的意思是如果你在最终的bundle jar中看见了重复的class和它所在的jar,说明你在Embed-Dependency
和Export-Package
中的定义重叠了。换句话说就是,你同时配置了Embed-Dependency
和Export-Package
,Export-Package将包中的class解压到bundle jar,Embed-Dependency将jar复制到bundle jar中。
如果你仅仅是想在最终的bundle jar的MANIFEST.MF的Export-Package头中导出包,并不想同时将包中的class解压到bundle中,你需要使用<_exportcontents>替代Export-Package,具体做法:
- 移除<Export-Package>
- 在Embed-Dependency之后添加<_exportcontents>,例如:
<Embed-Dependency>*</Embed-Dependency>
<_exportcontents>org.apache.commons.*</_exportcontents>
参考资料
Apache Felix Bundle Plugin Frequently Asked Questions
使用BND构建包含纯第三方依赖的Eclipse插件