OSGi规范中文版(第5版 core R6.0.0)-第3章模块层(Module Layer)3[译]

3.5 类加载机制

许多Bundle可以共享一个虚拟机(VM),具体可参考[1] Java Virtual Machine Specification, Second Edition。在VM内部,Bundle可以与其他Bundle之间隐藏package/class,以及共享package。

隐藏和共享package的关键因素由java类加载器实现,类加载器则通过bundle-space详细定义的规则加载类。每一个Bundle只会有一个单独的类加载器,类加载的代理网络结构模型如图3.4。
QQ_20140617150118
图 3.4 类加载代理模型

类加载器可以通过如下方式加载class和resource:

  • Boot class path – boot classpath包含java.* package和对应的实现package。
  • Framework class path –Framework通常有一个独立的类加载器,加载Framework实现的类以及关键服务接口。
  • Bundle Space –Bundle space由一系列相关的JAR文件组成,或者是紧密相关的JAR文件片段(具体可参考3.14Fragment Bundles)

类空间是指一个给定的bundle类加载器可以访问到的所有的类。故,一个指定的Bundle类空间包含如下内容:

  • parent类加载器(通常来自boot classpath中的java.* package)
  • Import package
  • Required bundle
  • Bundle的class path(private package)
  • 附加的fragment

类空间必须具有一致性,也就是说不能存在同名的2个class(为了防止类声明错误),但是,在OSGi框架中,不同的类空间可以存在同名的类。在模块层,支持不同版本的类,加载到相同的虚拟机中。
图3.5展示了BundleA的类空间,BundleA的右侧顶部不在类空间内,因为这部分描述被export替代的内容BundleA自身不可访问。
QQ_20140617150345
图3.5 类空间

在类加载过程中,Framework需要完成一系列的职责,在使用Bundle之前,必须对共享的package约束关系进行解析,选择一个最合适的类并创建wiring,具体可以参考3.8解析过程一节,在3.9运行期类加载一节中也描述了一些运行期的特性。

3.5.1 解析过程(resolving)

Framework必须支持Bundle解析。解析过程是指如何处理import包和export包的wire关系。解析过程需要满足约束条件(manifest中的Import/Export Package, Require-Bundle, Fragment-Host),而且resolving必须在Bundle中的代码加载或运行之前。
Wire是指exporter 和importer(都是Bundle)之间的实际关联关系,wire还关联到一系列的约束条件,这些约束都在manifest中定义,一个有效的wire必须满足对应所有约束,下图3.6描述了wiring模型的类结构
3_6
图3.6 wiring模型的类结构示例

3.6 Metadata解析

这一节定义了manifest header为resolver提供的metadata。

3.6.1 Bundle-ManifestVersion

Bundle manifest必须指定version,而且需要遵循OSGi manifest header语法,于是这个version由Bundle-ManifestVersion header提供。使用本规范以及后续版本的Bundle必须指定Bundle-ManifestVersion,语法如下:
Bundle-ManifestVersion ::= number // See 1.3.2

Framework中Bundle的manifest版本必须是’2’,以前版本Bundle的manifest版本为’1’,通常在这样的情况manifest中是无法表描述的。因此,如果版本值不为’2’时,Framework必须明确指明支持后续版本,否则都是无效的标记。
OSGi Framework的实现也可以支持没有Bundle-ManifestVersion的manifest,这样可以与Framework 1.2兼容。
版本为’2’的Bundle必须指定Bundle的Symbolic Name,而且不需要指定Bundle的版本,这个header头可以有默认值。

3.6.2 Bundle-SymbolicName

Bundle-SymbolicName manifest header必须指定。通过Bundle的symbolic name和bundle version确定一个唯一的Bundle;也就是说2个Bundle有同样的symbolic name和bundle version,那么这2个Bundle在Framework中是同一个Bundle。具体参考4.4.1 Bundle标识符

唯一的symbolic name由开发者确定(manifest中的Bundle-Name设计需要具有可读性),Bundle-SymbolicName语法如下:
Bundle-SymbolicName ::= symbolic-name
( ';' parameter ) * // See 1.3.2

Framework必须识别如下Bundle-symbolicName中的指令:

  • singleton –表示这个Bundle只有一个版本可以resolve。如果这个值是true,那么表示这是一个单例的Bundle,默认值是false。当同名并且设置为singleton的Bundle的多个版本同时安装在Framework中时,那么Framework最多只能对一个Bundle进行resolve。这是如果2个Bundle的symbolic name相同,那么singleton的Bundle并不会影响到非singleton的Bundle的处理。
  • fragment-attachment –定义了哪些fragment可以附加到Bundle上,参考3.14Bundle片段一节,下面定义了哪些值是合法的:
  • always –片段可以在host解析完成之后,或者解析过程中附加上去。
  • never –不允许附加片段
  • resolve-time –片段必须在解析过程中附加
  • mandatory –提供mandatory属性列表,表示如果这些不是专门用于requirement (Require-Bundle, Fragment-Host),那么Bundle不会被匹配,具体参考3.7.7 Mandatory属性
  • bundle-version –Bundle-Version header值或者0,如果没有这个header,那么表示此属性设置是错误的。 示例: Bundle-SymbolicName: com.acme.foo;singleton:=true

3.6.3 Bundle-Version

Bundle-Version是一个可选header,默认值是0.0.0
示例:Bundle-Version ::= version // 参考 3.2.5
如果小版本号(minor)和微版本号(micro)没有指定,那么默认值为0。如果限定部分没有指定,那么默认值是空字符串("")。
版本是可以进行比较的,比较规则按主版本号、小版本号、微版本号的顺序依次进行。最后是字符串的限定符比较。
示例:
Bundle-Version: 22.3.58.build-345678

3.6.4 Import-Package

Import-Package header定义了共享包的import约束。Import-Package的语法如下:
Import-Package ::= import ( ',' import )*
import ::= package-names ( ';' parameter )*
package-names ::= package-name
( ';' package-name )* // 参考 1.3.2
在header中可以定义多个import package,每条import定义描述了一个bundle中单独的package,多个package以分号方式分割。

Import package指令如下:

  • resolution – 如果值是mandatory,那么这个package必须被resolve,这个是默认值。如果mandatory的package不能被resolve,那么这个Bundle resolve会失败。Optional表示这个package是可选的。具体参考可选3.7.4 Package一节。 开发者可以指定特定的匹配属性,具体参考3.7.6 属性匹配.这些特定的匹配属性预定义如下:
  • version – 选择export package版本的版本范围,语法定义在3.2.6 版本范围。更多版本选择信息参考3.7.3 语义版本。如果这个属性没有指定,那么默认值是[0.0.0, ∞)。
  • specification-version – 这个属性是verison属性的别名,从早期版本中迁移过来。如果version属性已经被指定,那么这2个值必须相等。
  • bundle-symbolic-name – Bundle的标记名。在Bundle片段中,这个是指主Bundle的标记名。
  • bundle-version – export Bundle的版本范围。默认值是[0.0.0, ∞)。具体参考3.7.3语义版本。在Bundle片段中,这个是指主Bundle的版本。

为了允许import package(除java.开头的package),Bundle必须有PackagePermission[, IMPORT]。更多信息参考package权限一节。
当出现以下任意一个错误时将会终止安装或者更新:

  • 一个指令或者属性多次重复出现
  • 对同一个package多次重复定义import
  • version或者specification-version属性不匹配 正确定义示例如下: Import-Package: com.acme.foo;com.acme.bar; version="[1.23,1.24]";resolution:=mandatory

3.6.5 Export-Package
Export-Package头语法与Import-Package头的语法类似,只是一些指令和属性不同。
Export-Package ::= export ( ',' export )*
export ::= package-names ( ';' parameter )*
package-names ::= package-name ( ';' package-name )* // 参考 1.3.2
这个header允许多个package被export。每条export定义描述了一个bundle中单独的package,多个package以分号方式分割。多个重复定义的export package是允许的(不同的import属性都需要这些不同的export 版本)。

Export指令:

  • uses – export package需要使用的package列表。注意值中有逗号使用时,这个值必须被闭合的双引号包含。如果export package被选中,那么resolver必须确保import里的package关联了正确的版本,具体参考3.7.5 package约束。
  • mandatory – 逗号分隔的属性名列表。注意值中有逗号使用时,这个值必须被闭合的双引号包含。Bundle import package必须指定mandatory属性匹配的值用于export pakage解析。具体参考3.7.7 Mandatory属性
  • include – 逗号分隔对import的可见列表,注意值中有逗号使用时,这个值必须被闭合的双引号包含。具体参考3.7.8 Class Filter
  • exclude – 逗号分隔对import的不可见列表,注意值中有逗号使用时,这个值必须被闭合的双引号包含。具体参考3.7.8 Class Filter

以下属性是这节规范的一部分:

  • version – package version语法定义在3.2.5 version。默认值是0.0.0
  • specification-version – version的别名

另外,特定的匹配属性可以被指定。Framework会自动关联每个export package,export package定义如下属性:

  • bundle-symbolic-name –export Bundle的标记名。
  • bundle-version – export Bundle的version。 当下面任意的一个条件成立时,安装或者更新将会被终止:
  • 一条指令或者多个属性重复出现多次
  • bundle-symbolic-name或者bundle-version属性被指定在Export-Package头里。

export定义并不是自动import定义。一个export package但是没有import package的Bundle,将会通过bundle的class path获得package。对于只有export package的Bundle,这个Bundle只能被其他Bundle使用,不会接受从另外一个Bundle来的package,这个Bundle要import package优先从自己的class path中选择。
对于export package,Bundle必须有PackagePermission[, IMPORT]。
示例:
Export-Package: com.acme.foo;com.acme.bar;version=1.23

3.6.6 导入导出package

Bundle之间协作要求使用相同的classloader用于协作类型。如果多个Bundleexport的package放在不相交的类空间,那么他们之间无法协作。当Bundle需要import exported 的package时需要明显改善协作,而这些import允许framework来代替export和import。
不能通过替换来增强协作,导入export package只能工作于没有实现细节API的环境,故只在以下几种情况Import of exported packages:

  • export package没有使用私有package。
  • 至少有一个私有package引用export package,如果没有这个引用存在,那么这个import是没有意义的。

在实际中,importing exported packages只能使用干净的API实现分离。OSGi服务都是尽可能的设计成独立的。很多库的API和实现混合在一起并且在同一个package下以至不能很好替换API package。导入一个export过的package必须根据兼容性要求提供一个版本范围,无论是consumer或者provider的API。具体参考3.7.3语义版本一节

3.6.7 遗留Bundle

如果Bundles的Bundle-ManifestVersion的值不是2或比2大,则必须按照版本3中定义的header头。框架必须将版本3中的头映射为版本4中合适的header:

  • Import-Package –一个引入定义必须修改规范版本属性为版本属性。不含规范属性的引入定义不需要做任何修改,因为版本3中的默认值也是0.0.0
  • Export-Package – 一个输出定义必须修改规范版本属性为版本属性。输出定义必须附加指令uses。uses指令必须包含所有为这个给定的bundle引入的和输出的packages。另外,如果package没有引入定义,那么必须为它添加一个含有给定版本的引入定义。
  • DynamicImport-Package – 动态引入定义没有变化。 一个bundle的manifest包含了多个版本的混合语法是错误的,并且必须是这个bundle的安装失败。在版本2的headers中,属性specification-version是deprecated的,应该使用version来代替它。

3.7 约束解决

OSGi Framework package resolver提供了一系列的匹配import和export的机制,本节详细描述这些机制。

3.7.1 图表和语法

线(wire)和节点(node)组成图标,和Bundle一样,包含了大量重要信息。在下一节中,采用以下约定来阐述详细信息:
Bundle的名字为A,B,C…(即从A开始的大写字母)。Package名字则使用p, q, r, s, t,...(即从p开始的小写字母)。如果version很重要,那么在后面在一个短横线,如q-1.0。语法A.p表示Bundle A中定义了(import或export) package p。
import采用白色框来表示,export则使用黑色框来表示,没有import和export的包称之为private package,用斜线网格背景表示。
Bundle是一系列关联方框的集合。Bundle之间的关联用线(wire)表示,而约束条件则写在这些线上。
3_7
图3.7

例如:
A: Import-Package: p; version="[1,2)"
Export-Package: q; version=2.2.2; uses:=p
Require-Bundle: C
B: Export-Package: p; version=1.5.1
C: Export-Package: r

图 3.8 描述了A,B,C这3个Bundle的关系
3_8
图 3.8 Bundle图示例

3.7.2 版本(version)约束

版本约束定义import采用精确的版本号或者版本范围描述来匹配export。
一个import定义必须指定一个(import)版本范围,就像属性version一样,并且输出者必须指定一个(export)版本属性。版本范围匹配规则具体参考3.2.6版本范围一节。
例如以下的import和export定义可以被正常解析:
A: Import-Package: p; version="[1,2)"
B: Export-Package: p; version=1.5.1
如图3.9描述了这个约束关系
3_9
图3.9 版本约束

3.7.3 语义版本

版本范围通过编码考虑兼容性,OSGi framework并没有定义具体兼容性编码原则,强烈建议使用下面的语义。
传统意义上,兼容性是指2者之间的相容性,一个是指consumer的代码,另一个是指provider的代码,API兼容性基础设计具体为3部分:

  • API 自身
  • provider API
  • consumer API

Provider API是一个密切相关的API,几乎所有的API变化,都会使得provider需要实现不兼容的新版本的API,然而,API的变化从consumer角度则会有更多的回旋余地。许多API可以改变consumer的向后兼容性,但几乎没有任何API可以改变provider的向后兼容。
provider API和consumer API的兼容规则如下:

  • major – 不兼容更新 (consumer API和provider API)
  • minor –consumer API向后兼容更新,除了provider API.
  • micro – 不影响API的改变,例如,错误的注释.

Consumers和Providers都应该使用他们的编译版本作为基础版本,建议忽略微版本号,当修复部署最近的bug微版本号时会使得系统变得生硬,例如,编译版本4.2.1.V201007221030,基础版本应该为4.2
一个Consumer API应该import基础版本的起始和主版本号的结束,如[4.2,5)。Provider应该import到下一个次版本号的结束,如[4.2,4.3)。

3.7.4 语义版本类型角色

在语义版本中提到客户端API package有2种角色:
API consumers 和API providers。API消费者为API接口或者抽象类,API提供者则是API实现。对API提供者来说清晰的API文档描述非常重要,因为API提供者需要根据文档来实现API。


3.7.4 可选package

Bundle可以指明它不需要正确解析的package,但是这个package如果可用则使用之。例如,登录过程是很重要的,但是如果没有登录服务,Bundle也是可以正常运行的。
可选import package可以通过以下方式指明:

  • Dynamic Imports –DynamicImport-Package头描述了需要的export package。如果Bundle在调用之前不知道具体的类,大多采用Class.forName方法处理。
  • Resolution指令–在import中定义一个resolution指令并且指定一个可选值。如果没有合适的可选package,那么Bundle也是可以正常解析的。 Resolution和DynamicImport这2种机制的区别:对于动态导入,每次加载包中的类都会尝试与动态导入包建立连接,而可选导入包只有在bundle解析的时候才进行连接尝试。导入定义的resolution指令中,它的值可以为mandatory(强制的)或者是optional(可选的)。
  • mandatory – (默认值)表示Bundle解析的时候,package必须连接到这个Bundle。
  • optional – 指定package没有被连接到时,Bundle也能够被解析,即类加载的时候这个package不会被import进去。

下面的示例显示了Bundle B没有提供正确可匹配版本的情况下,Bundle A也能够被解析.
A: Import-Package: p; resolution:=optional; version=1.6
B: Export-Package: p; q; version=1.5.0
3_10
图 3.10 可选import

在Bundle实现中,需要考虑可选package没有加载的情况,如果抛出一个找不到package的异常信息,这可以帮助在Bundle classpath中对package做回退处理。当可选package不能被resolve时,任何通过这个package尝试加载的import package都是不存在的。
Bundle的package类可以通过Bundle classpath或者动态加载。

3.7.5 package约束

一个类可以依赖于其他package中的类。例如,继承自其他package中的类,或者这些类出现在方法声明中,这种情况可以称为一个package使用其他package。这种package之间的关系通过在Export-Package中使用uses指令实现。
例如,org.osgi.service.http依赖使用javax.servlet,他们之间的这个关系描述成org.osgi.service.http;uses:= "javax.servlet"

为了保证类空间的一致性,需要设置Bundle中的每一个package只有对应的一个export。例如,Http服务实现需要继承javax.servlet.http.HttpServlet这个基础类,如果Http服务Bundle需要import版本2.4而实际import了2.1版本时,必然会发生class cast异常。如图3.11
3_11
图 3.11 Uses 指令A,B需要使用D中的javax.servlet

如果Bundle从exporter中import package,那么export可以通过uses指令定义一些对其他package的约束。uses指令列出了exporter中依赖的一系列package,这些约束保证了这一系列Bundle对于相同的package共享同一个classloader。
如果importer导入了含有uses约束的package,resolver必须通过约束来建立import和export之间的连接。Exporter也有同样的约束。单个import package和exporter之间可以有很多约束。
implied package约束是指通过递归循环构建的约束集合,Implied package约束不是自动导入。更进一步,implied package约束只包括了必须解析的内容(在import中定义描述的内容)。
如图3.12,Bundle A import package p,这里假设定义的p连接到Bundle B。由于uses指令(这里省略uses指令描述)隐含了对package q的约束。进一步说,假设对package q的import关联到Bundle C,那么也就隐含了对package r和s的约束。接下来,假设C.s和C.r分别连接到Bundle D和E,那么这两个Bundle都将package t添加到Bundle A的依赖包集合中。
3_12
图3.12 Implied Packages

为了维护类空间的一致性,Framework必须确保Bundle的import不能和任何Bundle的implied package存在冲突。
例如,Framework必须保证A.t的import定义连接到D.t。这时如果import定义连接到package F.t,这就违背了类空间一致性。由于在Bundle A中同时存在来自Bundle D和F的类,这会导致ClassCastException。或者,如果让所有Bundle都连接到F.t,则也可以解决这个问题。
另一种情况如图3.11,Bundle A import了B中的Http服务,Bundle B包含org.osgi.service.http和the javax.servlet,而Bundle A和Bundle B都有连接到javax.servlet的约束。

下面的uses指令的设置导致不能正常解析示例:
A: Import-Package: q; version="[1.0,1.0]"
Export-Package: p; uses:="q,r",r
B: Export-Package: q; version=1.0
C: Export-Package: q; version=2.0

当A.q连接到B.q,而不是C.q时,这个时候能够解析。如果添加Bundle D则会导致不能解析:
D: Import-Package: p, q; version=2.0
D.q必须连接A.p,但是,A.p中包含uses指令,以至A.q连接到B.q-1.0,而D.q需要2.0版本,这违背了类空间约束。
图3.13描述了这种情况
3_13
图3.13 uses指令和解析

3.7.6 属性匹配

属性匹配允许importer和exporter通过说明性方式影响匹配过程处理。为了使得import定义可以解析为export定义,import中定义的属性值必须要和export中定义的属性值匹配。在默认情况下,如果export中包含的属性没有出现在import定义中,匹配过程将会继续进行。如果在export定义中指定了mandatory指令(强制),Framework会在import中强制匹配这些属性。在resolve阶段,对于出现在字段DynamicImport- Package中的任何信息都是会被忽略处理的。
例如,下例的语句是匹配的:
A: Import-Package: com.acme.foo;company=ACME
B: Export-Package: com.acme.foo; company="ACME"; security=false

所有属性值都是通过字符串方式进行比较(忽略属性中开始和结尾部分的空格),除了version和bundle- version属性。version和bundle- version采用版本范围的比较方法。

3.7.7 Mandatory属性(强制属性)

属性有两种类型:mandatory(强制) 和optional(可选)。mandatory属性表示必须匹配的属性。optional属性表示在import时可以不考虑的属性,属性默认值为optional。
exporter可以在export定义中通过mandatory指令指定mandatory属性(指令包含了一个逗号分割的属性名称列表,表示在import描述中必须强制匹配的属性)。如下所示,import package和export package是不匹配的,由于在Bundle A中没有指定security属性:
A: Import-Package: com.acme.foo;company=ACME
B: Export-Package: com.acme.foo; company="ACME"; security=false; mandatory:=security

3.7.8 类过滤

exporter可以在export定义使用include和exclude指令限制类的访问范围。这两条指令的值都是逗号分割的类名列表。需要注意的是,逗号需要用双引号包括起来。类名不能包含package name,而且不能含有后缀.class。即类com.acme.foo.Daffy在指令中名称为Daffy。类名中可以含有通配符(’*’ \u002A)。

include指令,默认值为通配符(’*’ \u002A)(表示匹配所有的名称),即所有的类和资源。
exclude指令,默认值为空(表示没有匹配的名称)。如果指定了这两个指令的值,那么默认值就被覆盖了。
如果满足以下条件,表示类是可见的:

  • 在include中匹配一条记录,而且在exclude中没有记录可以匹配
  • 在其他情况下,load或者find class失败,classloder会抛出一个无法找到类的异常(ClassNotFoundException)。在include和exclude中,不考虑顺序。 下例是一个可见性示例: demo1必须小心使用filter。例如,模块中的一个新版本需要和旧版本兼容,那么就不应该将旧版本中没有filter的类和资源filter掉。例如,定义的package通常在正式package中有一个实现类,而正式package对定义package有访问权限。
package org.acme.open;
public class Specified {
        static Specified implementation;
        public void foo() { implementation.foo(); }
}
package org.acme.open;
public class Implementation {
        public void initialize(Specified implementation) {
             Specified.implementation = implementation;
        }
}

对于扩展Bundle的实现类必须是不可见的。这时可以将实现类排除在外,使得只有export Bundle可以访问这个类。export定义的标记描述如:
Export-Package: org.acme.open; exclude:=Implementation

3.7.9 Provider selection

Provider selection允许importer选择一个Bundle作为exporter。如果importer和exporter之间没有任何约束,那么使用Provider selection,而且importer与一个特定的exporter紧密关联,一般情况下是用于Bundle测试。为了降低连接的脆弱性,importer可以指定一个可选的版本范围。

Importer可以通过import的bundle-symbolic-name、bundleversion属性来选择一个exporter,并且在Framework中能够自动为每个export定义提供这些属性,但是这些属性不能在export定义中进行指定。
Export中的bundle-symbolic-name属性是指Bundle的符号名称,对应在header头中的Bundle-SymbolicName。bundle-version对应header头中的Bundle-Version值,默认值是0.0.0。
bundle-symbolic-name采用属性匹配方式,bundle-version则是以版本范围方式比较。Import必须是一个版本范围而export是一个版本号。

示例,下面的定义是可以匹配的:
A: Bundle-SymbolicName: A
Import-Package: com.acme.foo; bundle-symbolic-name=B; bundle-version="[1.41,2.0.0)"
B: Bundle-SymbolicName: B
Bundle-Version: 1.41
Export-Package: com.acme.foo
下面的示例是不能匹配的,因为Bundle B没有指定版本号,默认版本为0.0.0:
A: Bundle-SymbolicName: A
Import-Package: com.acme.foo; bundle-symbolic-name=B; bundle-version="[1.41,2.0.0)"
B: Bundle-SymbolicName: B
Export-Package: com.acme.foo;version=1.42

将package和Bundle强耦合在一起后,通过符号名称来选择一个exporter会导致结果脆弱。例如,如果一个exporter被重构为多个Bundle,那么所有涉及的importer都将需要修改,其他任意属性匹配由于独立性而不存在这个缺陷。
Bundle重构时由符号名称带来的脆弱性问题,可以使用与原Bundle同名的façade Bundle来解决部分问题。

3.8 解析过程

Resolving是指Bundle之间建立连接的处理过程,连接之间的约束由以下条件静态定义:

  • 任意一个bundle被resolve时,在相同的namespace下capability中任何一个mandatory requirement都必须匹配
  • Required的可执行环境定义在header头Bundle-RequiredExecutionEnvironment
  • Native代码
  • Import和export package(不考虑DynamicImport-Package)
  • Required bundle,具体定义参考3.13 Requiring Bundle
  • Fragment,提供内容和定义,在3.14 bundle Fragment有详细定义

Bundle如果满足以下条件,则可以被resolve:

  • Execution Environment – 在Bundle-RequiredExecutionEnvironment中至少提供一个可执行环境,具体参考8.2 osgi.ee Namespace
  • Native code – native代码依赖具体在Bundle-NativeCode指定,具体参考3.10 Loading Native Code Libraries

解析过程是一个约束求解算法,使用关联关系进行描述,解析过程也是一个求解空间的迭代搜索过程。
如果一个模块(module)在import和export中有相同的package定义,那么Framework需要决定如何选择,首先必须处理重复的import定义,下面是可能的结果:

  • external—如果resolve到另一个Bundle的export定义,那么不考虑这个Bundle中的重复export定义。
  • internal—如果resolve到这个模块中的export定义,那么不考虑这个模块的重复import定义。
  • 无法解析—没有匹配的export定义。这是由于开发者引起的错误,也就是说重复的export定义和对应的import定义不相匹配。

遇到下面的情况Bundle可以被resolve:

  • 所有mandatory requirement被满足
  • 所有mandatory import有连接
  • 所有mandatory required bundle可用以及对应的export有连接

连接建立需要满足以下几个条件:

  • importer的版本范围和exporter的版本相匹配。
  • importer具有exporter指定的所有强制属性。
  • importer的所有属性和exporter的相应属性匹配。
  • 如果连接到同一个exporter,那么也就隐式关联到同一个package。
  • 连接关联一个合法的exporter。

下面列表定义了优先级别,如果有多个选择,则根据优先级降序排列:

  • 一个已经resolve的exporter优先于一个未resolve的exporter。
  • 具有更高版本的exporter优先于一个低版本的exporter。
  • 具有较低Bundle ID的exporter优先于较高ID的exporter。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值