简而言之, 在 Gradle 管理的多模块项目中, 一个服务模块 (app/server) 引用必要的库模块 (lib) 主要有两种方式:
- 引用库模块项目本身
- 引用库模块打好的 jar 包
详细方式与案例见下文.
一 引用库模块项目本身
构建一个 demo, 我此处使用 Kotlin
写示例以便展示的代码足够简洁, 用 Java
或者其它 JVM 语言也是一样的
app 依赖 lib1, lib1 依赖 lib2, 目录结构如下:
shell
gradle-subproject-demo
├── app
│ ├── build.gradle.kts
│ └── src
│ └── main
│ └── kotlin
│ └── org.looko.app
│ └── App.kt
├── lib1
│ ├── build.gradle.kts
│ └── src
│ └── main
│ └── kotlin
│ └── org.looko.lib1
│ └── util
│ └── LogUtil.kt
├── lib2
│ ├── build.gradle.kts
│ └── src
│ └── main
│ └── kotlin
│ └── org.looko.lib2
│ └── component
│ └── MyLoggerComponent.kt
└── settings.gradle.kts
接下来在代码上达成 App.kt 依赖 LogUtil.kt 依赖 MyLoggerComponent.kt
根目录
主要是声明 rootProject 和子模块
如果是用 IDEA 的 New Mudule 创建的模块一般会自动生成不用修改
若没有则手动添加
settings.gradle.kts
kotlin
plugins {
// Apply the foojay-resolver plugin to allow automatic download of JDKs
id("org.gradle.toolchains.foojay-resolver-convention") version "0.7.0"
}
rootProject.name = "gradle-subproject-demo"
include("app")
include("lib1")
include("lib2")
模块 lib2
build.gradle
不需要改
在代码中定义一个简单的日志实现以供给 lib1 使用
MyLoggerComponent.kt
kotlin
package org.looko.lib2.component
import kotlin.reflect.KClass
interface MyLogger {
fun info(msg: String)
}
class MyLoggerFactory {
companion object {
fun getLogger(clazz: KClass<*>): MyLogger =
object : MyLogger {
override fun info(msg: String) {
println("${clazz.simpleName} info: $msg")
}
}
}
}
模块 lib1
build.gradle.kts
直接引用模块
kotlin
dependencies {
api(project(":lib2"))
...
}
注意: 此处使用 api
是带依赖传递的, 之后在 app 模块中只需引入依赖 lib1 即可; 如果使用 implementation
则后面的 app 模块不仅要引入依赖 lib1 还要引入依赖 lib2
使用 lib2 的内容, 定义一个全局都能用的 logger 以供 app 使用
LogUtil.kt
kotlin
package org.looko.lib1.util
import org.looko.lib2.component.MyLogger
import org.looko.lib2.component.MyLoggerFactory
inline val Any.logger: MyLogger
get() = MyLoggerFactory.getLogger(this::class)
模块 app
build.gradle.kts
直接引用模块
kotlin
dependencies {
implementation(project(":lib1"))
...
}
若是如上面所说 lib1 依赖 lib2 时用的是 implementation
而非 api
, 则此处需要再写上 implementation(project(":lib2"))
在类中使用 lib1的内容
App.kt
kotlin
package org.looko.app
import org.looko.lib1.util.logger
class App {
val greeting: String
get() {
logger.info("success")
return "Hello World!"
}
}
fun main() {
println(App().greeting)
}
运行后即可成功输出:
shell
App info: success
Hello World!
二 引用库模块打好的 jar 包
如果这个库除了给同一项目的兄弟模块使用之外, 还想要给其它项目使用, 可以打成 jar 包到依赖仓库中
这便是大多数人习惯的通用的依赖管理方式了
在示例 Kotlin 项目中, 由于是使用 Gradle 作为构建工具, 同样可以使用 maven-publish
插件进行打包
比如 lib2 模块里的配置文件可以这么写
build.gradle.kts
kotlin
plugins {
kotlin("jvm") version "1.9.20"
`maven-publish`
}
group = "org.looko"
version = "0.0.1-SNAPSHOT"
val sourcesJar by tasks.registering(Jar::class) {
archiveClassifier.set("sources")
from(sourceSets.main.get().allSource)
}
publishing {
publications {
register(project.name, MavenPublication::class) {
groupId = project.group as String
artifactId = project.name
version = project.version as String
from(components["java"])
artifact(sourcesJar.get())
}
}
repositories {
maven {
mavenLocal()
}
}
}
另外如果是 Spring Boot (Starter) 项目, 打出来的包可能会带 plain
后缀, 一些项目可能不识别, 可以在build.gradle.kts
下加如下任务:
kotlin
// 将可执行 jar 包加以区分
tasks.named<BootJar>("bootJar") {
archiveClassifier.set("boot")
}
// 清除 plain 后缀
tasks.named<Jar>("jar") {
archiveClassifier.set("")
}
如果打出的包可以正常引用则不用加
总结
上述介绍了在 Gradle 多模块项目中的两种引用依赖的方式
两种方式均可以在普通 Libaray 类或者是 SpringBootStarter 类中使用