第十六天:复数专案架构

之前在看别人的 JVM 专案时,有时会发现数个不同的 Module 原始码却都指向同一个 Repository,这才发现原来 Gradle 支援复数专案架构。也就是说在一个 Repository 里可以指定不同子资料夹放置不同的子专案,而 Gradle 在建置专案的时候,会依照这样的专案架构输出多个结果,并发布到不同的 Module 名称底下。

若大家还记得在我们一开始学习 Gradle 的 init 指令时,也曾经被询问过是否要建立多个子专案。今天我们就来看一下 Gradle 是如何产生出复数专案架构的?另外也同场加映一下,看类似的复数专案架构要怎麽在 IntelliJ IDEA 里新增?

使用 Gradle 指令建立复数专案架构

首先开启终端机,先建立一个测试用的资料夹,并切换成工作目录,接着执行 $ gradle init,跟着互动式问答一步步建立专案,只是这次在询问是否要建立 Multiple Subproject 时要选 yes

$ gradle init

Select type of project to generate:
  1: basic
  2: application
  3: library
  4: Gradle plugin
Enter selection (default: basic) [1..4] 2

Select implementation language:
  1: C++
  2: Groovy
  3: Java
  4: Kotlin
  5: Scala
  6: Swift
Enter selection (default: Java) [1..6] 4

Split functionality across multiple subprojects?:
  1: no - only one application project
  2: yes - application and library projects
Enter selection (default: no - only one application project) [1..2] 2

Select build script DSL:
  1: Groovy
  2: Kotlin
Enter selection (default: Kotlin) [1..2] 2

Project name (default: multi-project-gradle):
Source package (default: multi.project.gradle): io.kraftsman

> Task :init
Get more help with your project: https://docs.gradle.org/7.2/samples/sample_building_kotlin_applications_multi_project.html

BUILD SUCCESSFUL in 25s
2 actionable tasks: 2 executed

专案建立完成後,我们会拿到一个像这样的专案架构:

├── app
│   ├── build.gradle.kts
│   └── src
│       ├── main
│       │   ├── kotlin
│       │   └── resources
│       └── test
│           ├── kotlin
│           └── resources
├── buildSrc
│   ├── build.gradle.kts
│   └── src
│       └── main
│           └── kotlin
├── list
│   ├── build.gradle.kts
│   └── src
│       ├── main
│       │   ├── kotlin
│       │   └── resources
│       └── test
│           ├── kotlin
│           └── resources
├── utilities
│   ├── build.gradle.kts
│   └── src
│       ├── main
│       │   ├── kotlin
│       │   └── resources
│       └── test
│           └── resources
├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle.kts

在这个结构里 applistutilities 各自代表 Application、Library、Utilities 专案,这些资料夹都有类似的结构,都有自己的 build.gradle.ktssrc 资料夹里都有标准的 Module 结构,一个 main、一个 test 资料夹,里面各自有 kotlinresources 资料夹。而在最外层则是共享 Gradle Wrapper(gradle 资料夹及 2 个 gradlew)及 settings.gradle.kts。最後 buildSrc 是 Gradle Plugin,我们现阶段先暂时略过。

打开 settings.gradle.kts,会发现它将 3 个子资料夹用 include() 的方式汇入:

rootProject.name = "multi-project-gradle"
include("app", "list", "utilities")

使用 IntelliJ IDEA 建立复数专案架构

接着我们用 IntelliJ IDEA 试着建立类似的复数专案架构。点选 File > New > Project...,依序填入 Name、Location、Project Template、Build System、Project JDK 等资料。

下一步先设定 mainModule 的 Template、Test Framework、Target JVM version。

然後再按一下左上角的 + 按钮就可以新增一个 Module,我们仿照 Gradle 的结构新增 2 个新的 Module。

再回到 mainModule,更名为 app,并设定与另外两个 Module 相依。

专案建立好後,整个结构长得像这样:

├── app
│   ├── build.gradle.kts
│   └── src
│       ├── main
│       │   ├── kotlin
│       │   └── resources
│       └── test
│           ├── kotlin
│           └── resources
├── list
│   ├── build.gradle.kts
│   └── src
│       ├── main
│       │   ├── kotlin
│       │   └── resources
│       └── test
│           ├── kotlin
│           └── resources
├── utilities
│   ├── build.gradle.kts
│   └── src
│       ├── main
│       │   ├── kotlin
│       │   └── resources
│       └── test
│           ├── kotlin
│           └── resources
├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── build.gradle.kts
├── gradle.properties
├── gradlew
├── gradlew.bat
└── settings.gradle.kts

从上面的结构比对起来,专案结构是类似的,每个子专案都有自己的 build.gradle.ktssrc 资料夹。外层也有相同的 Gradle Wrapper、settings.gradle.kts。唯一的差别在於,IntelliJ IDEA 会帮我们在外层放 build.gradle.ktsgradle.properties

打开 build.gradle.kts 观察一下,会发现 IntelliJ IDEA 会帮我们针对全部的专案设定共享的 repositories 设定,这样在子专案里就可以省下重复宣告的工作:

allprojects {
    repositories {
        mavenCentral()
    }
}

最後还想提一点,就是当我们是复数专案时,IntelliJ IDEA 的 Gradle 面板就会把各个子专案的 Gradle Tasks 以分资料夹的方式呈现,在使用的时候,要注意一下自己呼叫的是哪个子专案的任务。

而若是从终端机输入指令的话,则要使用 :<project>:<task> 的组合来指定正确的任务全名。


<<:  Youtube Data API 教学 - 基本分类介绍 list.part

>>:  【领域展开 06 式】 WordPress 主机商与网域选购

[ Day 2 ] - 变数与型别(二)

变数与型别(二) 变数 简单回顾一下,变数的宣告方式 let catNum = 3; 用比较口语的方...

人的管理 - 危机感 vs. 安全感

我在 MIT 的两年改变了我很多。其中一个重要的体验是有很多世界级的创业家、执行长会来学校演讲,甚至...

30天零负担轻松学会制作APP介面及设计【DAY 15】

大家好,我是YIYI,今天我要来制作日记-LIST以及个人资料页面。 日记LIST 日记LIST是在...

邀约演讲经验谈

今天要跟各位分享的是我经营粉专後发生的故事! 进入正题 在经营粉专的初期,我维持约 2-3 天产出一...

Day25 ( 游戏设计 ) 翻转吃豆子

翻转吃豆子 教学原文参考:翻转吃豆子 这篇文章会介绍如何使用「旋转感测」、「创建角色」、「得分」、「...