biz.aQute.bnd依赖介绍
1. biz.aQute.bnd简介:
-
biz.aQute.bnd是一个用于创建和处理OSGi捆绑包的工具集
-
它提供了一系列工具和插件,用于生成OSGi元数据、验证OSGi清单和解析OSGi依赖
2. 在OkHttp项目中的应用:
-
OkHttp项目使用biz.aQute.bnd来生成OSGi兼容的JAR文件
-
通过buildSrc模块中的Osgi.kt文件定义了应用OSGi配置的函数
-
主要函数包括applyOsgi和applyOsgiMultiplatform,用于不同类型的项目
3. OSGi配置:
-
在Osgi.kt中定义了OSGi捆绑包的各种属性,如Bundle-SymbolicName、Bundle-Version等
-
配置了导出包和导入包的规则
-
处理了多平台项目的特殊需求
4. 测试:
-
okhttp-osgi-tests模块专门用于测试OkHttp库在OSGi环境中的兼容性
-
使用aQute.bnd.build和biz.aQute.resolve包来创建OSGi工作空间和解析依赖
-
测试确保所有OkHttp模块都有正确的OSGi元数据
5. 依赖:
-
buildSrc/build.gradle.kts中引入了biz.aQute.bnd.builder插件
-
项目依赖了多个bnd相关库,如biz.aQute.bndlib和biz.aQute.resolve
6. OSGi的重要性:
-
OSGi允许Java应用程序在运行时动态安装、启动、停止和更新模块
-
对于像OkHttp这样的库,OSGi兼容性意味着它可以在OSGi容器(如Eclipse Equinox、Apache Felix等)中使用
-
这增强了库的适用性,特别是在企业环境中
buildSrc模块与biz.aQute.bnd分析
buildSrc模块概述
buildSrc是OkHttp项目中的一个特殊Gradle模块,用于集中管理构建逻辑和自定义任务。这个模块中的代码会自动编译并添加到Gradle构建脚本的类路径中,使其可以在项目的所有build.gradle.kts文件中使用。
在OkHttp项目中,buildSrc模块主要包含:
-
构建配置逻辑
-
自定义Gradle任务
-
OSGi相关配置(通过Osgi.kt)
-
ALPN版本管理(通过AlpnVersions.kt)
biz.aQute.bnd详解
什么是biz.aQute.bnd?
biz.aQute.bnd是一个强大的OSGi工具集,由Peter Kriens创建,用于简化OSGi捆绑包的创建和管理。它提供了:
1. OSGi清单生成:自动从类文件和依赖中生成OSGi元数据
2. 版本管理:处理语义化版本和包版本
3. 依赖解析:分析和解析OSGi依赖
4. 构建工具集成:与Gradle、Maven等构建工具的集成
OkHttp中的biz.aQute.bnd配置
在OkHttp项目中,biz.aQute.bnd的配置主要通过buildSrc/src/main/kotlin/Osgi.kt文件实现:
// 为标准Java/Kotlin项目应用OSGi配置
fun Project.applyOsgi() {
// 应用biz.aQute.bnd.builder插件
apply(plugin = "biz.aQute.bnd.builder")
// 配置OSGi捆绑包属性
configure<BundleExtension> {
bnd(
mapOf(
"Bundle-SymbolicName" to "com.squareup.${project.name}",
"Bundle-Name" to project.name,
"Bundle-Description" to "Square's ${project.name} library.",
"Bundle-Vendor" to "Square, Inc.",
"Bundle-Version" to project.version,
"Export-Package" to "!*.internal.*,${project.name}.*",
"Import-Package" to "!javax.annotation.*,*",
"Automatic-Module-Name" to "com.squareup.${project.name.replace('-', '.')}"
)
)
}
}
// 为多平台项目应用OSGi配置
fun Project.applyOsgiMultiplatform() {
// 多平台项目的特殊处理逻辑
// ...
}
OSGi测试实现
OkHttp项目包含专门的okhttp-osgi-tests模块,用于验证库在OSGi环境中的兼容性:
1. 测试环境设置:
-
创建临时OSGi工作空间
-
部署必要的OSGi捆绑包
-
配置Eclipse Equinox作为OSGi框架
2. 依赖解析测试:
@Test
fun testMainModuleWithSiblings() {
createWorkspace().use { workspace ->
createBndRun(workspace).use { bndRun ->
bndRun.resolve(false, false)
}
}
}
此测试验证所有OkHttp模块都具有有效的OSGi元数据,并且可以在OSGi环境中正确解析。
3. 测试的捆绑包:
private val REQUIRED_BUNDLES: List<String> = mutableListOf(
"com.squareup.okhttp3",
"com.squareup.okhttp3.brotli",
"com.squareup.okhttp3.dnsoverhttps",
"com.squareup.okhttp3.logging",
"com.squareup.okhttp3.sse",
"com.squareup.okhttp3.tls",
"com.squareup.okhttp3.urlconnection"
)
biz.aQute.bnd的重要性
为什么OkHttp需要OSGi支持?
1. 广泛的兼容性:
-
OSGi是Java生态系统中重要的模块化标准
-
许多企业应用和中间件使用OSGi容器(如Eclipse Equinox、Apache Felix)
-
通过支持OSGi,OkHttp可以在这些环境中无缝使用
2. 动态模块化:
-
OSGi允许在运行时动态安装、启动、停止和更新模块
-
这对于长时间运行的应用程序(如服务器)特别有价值
3. 版本管理:
-
OSGi提供了严格的版本控制机制
-
允许同一JVM中共存多个版本的库
biz.aQute.bnd的优势
1. 自动化:
-
自动分析类文件和依赖关系
-
生成符合OSGi规范的清单文件
-
减少手动配置错误
2. 构建集成:
-
与Gradle无缝集成
-
作为构建过程的一部分生成OSGi元数据
3. 验证:
-
验证捆绑包的完整性
-
确保所有依赖都正确声明
结论
biz.aQute.bnd在OkHttp项目中扮演着关键角色,它使OkHttp库能够在OSGi环境中正常工作,从而扩大了库的适用范围。通过buildSrc模块中的自定义配置,OkHttp团队简化了OSGi相关的构建逻辑,确保所有模块都具有一致的OSGi元数据。
这种方法不仅提高了OkHttp的兼容性,还展示了现代Java库如何通过适当的工具和配置来支持不同的运行环境和模块化系统。
从构建流程理解buildSrc代码逻辑
1. buildSrc 模块概述
在 Gradle 项目里,buildSrc 是一个特殊模块。Gradle 执行构建时,会自动编译并将 buildSrc 模块中的代码添加到构建脚本的类路径中。这使得开发者能在 buildSrc 里编写自定义的插件、扩展和工具类,供整个项目的构建脚本使用。
2. Osgi.kt 文件在 buildSrc 模块中的作用
Osgi.kt 文件位于 buildSrc/src/main/kotlin 目录下,其主要功能是为 Gradle 项目提供配置 OSGi(Open Service Gateway Initiative) 的能力。OSGi 是一种 Java 动态模块化系统标准,可让 Java 程序以模块化方式开发和部署,支持模块的动态加载、卸载和更新。Osgi.kt 通过扩展 Gradle 的 Project 类型,提供了将项目打包成符合 OSGi 规范的 Bundle 包的功能。
3. 核心功能分析
3.1 扩展函数
Osgi.kt 定义了两个扩展函数,分别用于不同类型项目的 OSGi 配置。
3.1.1 applyOsgi 函数
fun Project.applyOsgi(vararg bndProperties: String) {
plugins.withId("org.jetbrains.kotlin.jvm") {
applyOsgi("jar", "osgiApi", bndProperties)
}
}
该函数适用于非 Kotlin/Multiplatform 项目。它会检查项目是否应用了 Kotlin JVM 插件,若应用了,则调用私有 applyOsgi 函数进行具体配置。
3.1.2 applyOsgiMultiplatform 函数
fun Project.applyOsgiMultiplatform(vararg bndProperties: String) {
// 处理 Kotlin/Multiplatform 项目的兼容性问题
val jvmMainSourceSet = sourceSets.getByName("jvmMain")
val mainSourceSet = object : SourceSet by jvmMainSourceSet {
// 重写部分方法
}
extensions.getByType(JavaPluginExtension::class.java).sourceSets.add(mainSourceSet)
tasks.named { it.endsWith("ForFakeMain") }.configureEach { onlyIf { false } }
val osgiApi = configurations.create("osgiApi")
project.dependencies {
osgiApi(kotlinOsgi)
}
tasks.named<Jar>("jvmJar").configure {
val bundleExtension = extensions.create(
BundleTaskExtension.NAME,
BundleTaskExtension::class.java,
this
).apply {
classpath(osgiApi.artifacts)
classpath(tasks.named("jvmMainClasses").map { it.outputs })
bnd(*bndProperties)
}
doLast {
bundleExtension.buildAction().execute(this)
}
}
}
此函数专门为 Kotlin/Multiplatform 项目设计。由于 BND 工具假设 JVM 源集名称为 main,而 Kotlin/Multiplatform 项目的 JVM 源集名称是 jvmMain,所以该函数创建了一个名为 main 的源集,将其转发到 jvmMain 源集,解决兼容性问题。同时,它还创建 osgiApi 配置,添加 Kotlin OSGi 依赖,并在 jvmJar 任务完成后添加 OSGi 元数据。
3.2 私有 applyOsgi 函数
private fun Project.applyOsgi(
jarTaskName: String,
osgiApiConfigurationName: String,
bndProperties: Array<out String>,
) {
val osgi = project.sourceSets.create("osgi")
val osgiApi = project.configurations.getByName(osgiApiConfigurationName)
project.dependencies {
osgiApi(kotlinOsgi)
}
val jarTask = tasks.getByName<Jar>(jarTaskName)
val bundleExtension = jarTask.extensions.findByType<BundleTaskExtension>()
?: jarTask.extensions.create(
BundleTaskExtension.NAME,
BundleTaskExtension::class.java,
jarTask
)
bundleExtension.run {
setClasspath(osgi.compileClasspath + sourceSets["main"].compileClasspath)
bnd(*bndProperties)
}
jarTask.doLast {
bundleExtension.buildAction().execute(this)
}
}
该私有函数负责具体的 OSGi 配置逻辑。它创建 osgi SourceSet,获取 osgiApi 配置并添加 Kotlin OSGi 依赖,然后为指定的 Jar 任务配置 BundleTaskExtension,设置类路径和 BND 配置属性,最后在 Jar 任务完成后添加 OSGi 元数据。
3.3 扩展属性
val Project.sourceSets: SourceSetContainer
get() = (this as ExtensionAware).extensions["sourceSets"] as SourceSetContainer
private val Project.kotlinOsgi: MinimalExternalModuleDependency
get() = extensions.getByType(VersionCatalogsExtension::class.java)
.named("libs")
.findLibrary("kotlin.stdlib.osgi")
.get()
.get()
sourceSets扩展属性:方便获取项目的SourceSetContainer。kotlinOsgi扩展属性:从版本目录中获取 Kotlin OSGi 依赖。
4. 工作流程
4.1 项目构建触发
开发者通过命令行(如 ./gradlew build)或 IDE 触发 Gradle 构建。
4.2 buildSrc 模块编译
Gradle 会自动编译 buildSrc 模块中的代码,并将其添加到构建脚本的类路径中。这意味着 Osgi.kt 里定义的扩展函数和属性在构建脚本里可以直接使用。
4.3 调用扩展函数
在项目的 build.gradle.kts 脚本中调用 applyOsgi 或 applyOsgiMultiplatform 函数,根据项目类型触发相应的 OSGi 配置逻辑。例如:
// 非 Kotlin/Multiplatform 项目
applyOsgi(
"Bundle-SymbolicName: com.example.myapp",
"Bundle-Version: 1.0.0",
"Export-Package: com.example.api"
)
// Kotlin/Multiplatform 项目
applyOsgiMultiplatform(
"Bundle-SymbolicName: com.example.myapp.multiplatform",
"Bundle-Version: 1.0.0",
"Export-Package: com.example.shared.api"
)
4.4 配置 OSGi 相关信息
根据项目类型,执行相应的配置逻辑:
- 非 Kotlin/Multiplatform 项目:创建
osgiSourceSet,获取osgiApi配置并添加 Kotlin OSGi 依赖,为Jar任务配置BundleTaskExtension,设置类路径和 BND 配置属性。 - Kotlin/Multiplatform 项目:创建名为
main的源集转发到jvmMain源集,解决兼容性问题;创建osgiApi配置,添加 Kotlin OSGi 依赖;为jvmJar任务配置BundleTaskExtension,设置类路径和 BND 配置属性。
4.5 生成 OSGi Bundle 包
在 Jar 任务(非 Kotlin/Multiplatform 项目)或 jvmJar 任务(Kotlin/Multiplatform 项目)完成后,调用 BundleTaskExtension 的 buildAction 方法,为生成的 JAR 文件添加 OSGi 元数据,最终生成符合 OSGi 规范的 Bundle 包。
5. 构建配置示例
5.1 非 Kotlin/Multiplatform 项目构建配置
在 build.gradle.kts 中调用 applyOsgi 函数:
plugins {
kotlin("jvm") version "1.9.20"
}
applyOsgi(
"Bundle-SymbolicName: com.example.myapp",
"Bundle-Version: 1.0.0",
"Export-Package: com.example.api"
)
5.2 Kotlin/Multiplatform 项目构建配置
在 build.gradle.kts 中调用 applyOsgiMultiplatform 函数:
plugins {
kotlin("multiplatform") version "1.9.20"
}
kotlin {
jvm()
}
applyOsgiMultiplatform(
"Bundle-SymbolicName: com.example.myapp.multiplatform",
"Bundle-Version: 1.0.0",
"Export-Package: com.example.shared.api"
)
6. 生成文件附加的配置信息
构建完成后,生成的 OSGi Bundle 包(通常是 JAR 文件)会在 META-INF/MANIFEST.MF 中包含附加的 OSGi 配置信息。以非 Kotlin/Multiplatform 项目构建配置示例为例,MANIFEST.MF 可能如下:
Manifest-Version: 1.0
Bundle-SymbolicName: com.example.myapp
Bundle-Version: 1.0.0
Export-Package: com.example.api
Import-Package: ...
这些信息由 applyOsgi 或 applyOsgiMultiplatform 函数中的 bndProperties 参数配置。
7. OSGi 插入构建的切入点对应的代码
7.1 非 Kotlin/Multiplatform 项目切入点
jarTask.doLast {
bundleExtension.buildAction().execute(this)
}
在 applyOsgi 函数中,jarTask.doLast 是插入点,在 Jar 任务完成后,调用 BundleTaskExtension 的 buildAction 方法添加 OSGi 元数据。
7.2 Kotlin/Multiplatform 项目切入点
tasks.named<Jar>("jvmJar").configure {
// ...
doLast {
bundleExtension.buildAction().execute(this)
}
}
在 applyOsgiMultiplatform 函数中,jvmJar 任务的 doLast 闭包是插入点,在 jvmJar 任务完成后添加 OSGi 元数据。
8. 总结
buildSrc 模块中的 Osgi.kt 文件通过扩展 Gradle 的 Project 类型,为项目提供了便捷的 OSGi 配置功能。开发者可以根据项目类型选择合适的扩展函数,在构建脚本中配置 BND 属性,最终将项目打包成符合 OSGi 规范的 Bundle 包。OSGi 配置通过在 Jar 任务完成后插入操作,为生成的 JAR 文件添加必要的元数据。