多模块Gradle项目的库模块依赖方式

简而言之, 在 Gradle 管理的多模块项目中, 一个服务模块 (app/server) 引用必要的库模块 (lib) 主要有两种方式:

  1. 引用库模块项目本身
  2. 引用库模块打好的 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 类中使用

相关推荐
zhangphil11 小时前
Android使用PorterDuffXfermode模式PorterDuff.Mode.SRC_OUT橡皮擦实现“刮刮乐”效果,Kotlin(2)
android·kotlin
居居飒1 天前
Android学习(四)-Kotlin编程语言-for循环
android·学习·kotlin
刘争Stanley2 天前
如何高效调试复杂布局?Layout Inspector 的 Toggle Deep Inspect 完全解析
android·kotlin·android 15·黑屏闪屏白屏
sickworm陈浩2 天前
Java 转 Kotlin 系列:究竟该不该用 lateinit?
android·kotlin
droidHZ4 天前
Compose Multiplatform 之旅—声明式UI
android·kotlin
zhangphil4 天前
Android基于Path的addRoundRect,Canvas剪切clipPath简洁的圆角矩形实现,Kotlin(1)
android·kotlin
流水随清风5 天前
IDEA 使用 Gradle 强制清除缓存,更新快照
java·ide·gradle·intellij-idea
alexhilton6 天前
Android技巧:学习使用GridLayout
android·kotlin·android jetpack
zhangphil7 天前
Android使用PorterDuffXfermode的模式PorterDuff.Mode.SRC_OUT实现橡皮擦,Kotlin(1)
android·kotlin
IH_LZH7 天前
OkHttp源码分析:分发器任务调配,拦截器责任链设计,连接池socket复用
android·java·okhttp·kotlin