[TOC]
前言
最近在学习如何使用gradle
,原本都是使用maven
的,但随着gradle
的广泛使用,开源项目基本上都改为gradle
了。之前就碰到,构建Elasticsearch
源码的时候,在gradle
中找不到版本号定义的尴尬,所以尝试学习一下gradle
。
这个会是一个系列,主要记录我使用和学习时碰到的问题、方法及开源项目是如何使用gradle
的。比较偏实战,希望自己可以保持学习、保持更新。
初步使用
从Maven
构建gradle
项目
当前我的目标主要是将一个内部项目改为使用gradle
,对于maven
项目,gradle
可以非常方便的直接进行转换,虽然后面还是需要少量的修改,但已经可以快速进行转换了。
我们先看一下在maven
下的目录:
1
2
3
4
5
6
7
8
9
10
|
├── HELP.md
├── honlyc-sample.iml
├── mvnw
├── mvnw.cmd
├── pom.xml
└── server
├── HELP.md
├── pom.xml
├── server.iml
└── src
|
这是直接使用Spring-Initler
初始化的一个项目,包含了一个子模块server
。接下来我们会在这个项目进行我们的gradle
学习和使用,首先,让我们进行转换。
在maven
项目的根路径,直接执行命令:
1
2
3
4
5
6
7
8
9
|
gradle init
// 执行后会提示你是否从 pom 构建:
Found a Maven build. Generate a Gradle build from this? (default: yes) [yes, no]
// 输入 yes ,会让选择使用 grovvy 还是 kotlin,我选的是 kotlin。
Select build script DSL:
1: Groovy
2: Kotlin
Enter selection (default: Groovy) [1..2] 2
// 这里选 kotlin,主要后面有地方需要用到 kotlin 相关的特性
|
执行完成后,就可以看到gradle
相关文件就已经创建好了。在看下目录结构:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
├── HELP.md
├── buildSrc
│ ├── build.gradle.kts
│ └── src
├── gradle
│ └── wrapper
├── gradlew
├── gradlew.bat
├── honlyc-sample.iml
├── mvnw
├── mvnw.cmd
├── pom.xml
├── server
│ ├── HELP.md
│ ├── build.gradle.kts
│ ├── pom.xml
│ ├── server.iml
│ └── src
└── settings.gradle.kts
|
可以看到,一键转换还是非常好用的,而且,我当前gradle
版本是6.7.1
,在构建时,自动给你使用了buildSrc
的方式,我们待会再详细讲解这个。
这时,我们在IDEA
里面打开任意build.gradle.kts
文件,就会提示你Link Gradle Project
,点击,即可在IDEA
里面使用gradle
编译了。
可以看到,在使用gradle
编译后,buildSrc
是自动编译成一个模块了。
好了,从maven
转换成gradle
,就暂时搞定,接下来就是各种实际的使用了。
buildSrc
分析
在看buildSrc
之前,我们先看一下server
模块的build.gradle.kts
文件:
1
2
3
4
5
6
7
8
9
|
/*
* This file was generated by the Gradle 'init' task.
*/
plugins {
id("com.honlyc.java-conventions")
}
description = "server"
|
咦,这里面只有一个plugins
和description
,那我的依赖呢?版本呢?带着这个疑问,我们再来看buildSrc
模块,buildSrc
模块也很简单,先看结构:
就两个文件,我们分别看:
build.gradle.kts
内容:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
/*
* This file was generated by the Gradle 'init' task.
*/
plugins {
// Support convention plugins written in Kotlin. Convention plugins are build scripts in 'src/main' that automatically become available as plugins in the main build.
`kotlin-dsl`
}
repositories {
// Use the plugin portal to apply community plugins in convention plugins.
gradlePluginPortal()
}
|
这个文件也很简单,依赖一个plugin
,定义了一个repositories
,还是没有看到我们想要的依赖、版本这些的定义。接着看另外一个文件,com.honlyc.java-conventions.gradle.kts
内容:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
/*
* This file was generated by the Gradle 'init' task.
*/
plugins {
`java-library`
`maven-publish`
}
repositories {
mavenLocal()
maven {
url = uri("https://repo.maven.apache.org/maven2/")
}
}
dependencies {
implementation("commons-dbutils:commons-dbutils:1.4")
implementation("commons-lang:commons-lang:2.6")
implementation("commons-collections:commons-collections:3.2.1")
implementation("commons-io:commons-io:2.1")
implementation("commons-logging:commons-logging:1.1.1")
testImplementation("junit:junit:4.12")
}
group = "com.honlyc"
version = "0.0.1-SNAPSHOT"
java.sourceCompatibility = JavaVersion.VERSION_1_8
publishing {
publications.create<MavenPublication>("maven") {
from(components["java"])
}
}
tasks.withType<JavaCompile>() {
options.encoding = "UTF-8"
}
|
终于,我们在这里看到了依赖、版本、仓库等内容,同时,我们也能够发现,这个我文件的命名是com.honlyc.java-conventions
开头,这跟我们在server
中依赖的插件名称一致。
其实,buildSrc
里面,这种写法就是自定义了一个gradle
插件,插件名称为com.honlyc.java-conventions
,这个插件里面定义了整个项目的仓库、名称、版本、公共依赖等一系列项目内共有的属性。其他所有的子模块,都只需要依赖这个插件,就可以直接应用到所有的内容,同时,也可以在子模块内部有自己的依赖。
这样,在管理上,就一目了然了;在结构上,因为引入了buildSrc
,所以也没有了跟settings.gradle.kts
同级的build.gradle.kts
文件的存在;
关于buildSrc
,我这里只是简单介绍,它还有很多使用技巧,后续我会陆续进行记录的。
lombok
使用
相信很多小伙伴在项目中都会用到lombok
这个工具,而在gradle
中,它有不同的引入方式。我们这个项目因为使用了buildSrc
,在使用上,又会有所不同,接下来,让我们一起来看看怎么用起来吧。
首先,我们要在buildSrc/build.gradle.kts
文件中添加一个依赖:
1
2
3
|
dependencies {
implementation("io.freefair.gradle:lombok-plugin:6.0.0-m2")
}
|
表示buildSrc
这个项目依赖了lombok
,但这个时候,这个依赖并不会传递到server
等子模块上去,我们需要在插件里面去引用才行。
编辑buildSrc/../com.honlyc.java-conventions.gradle.kts
文件,添加一个插件依赖:
1
2
3
4
5
|
plugins {
`java-library`
`maven-publish`
id("io.freefair.lombok") // 添加的 lombok 插件,这里不需要写版本号,因为在外面已经依赖了
}
|
gradle build
一下,我们就可以在server
模块直接使用lombok
啦:
1
2
3
4
|
@Data
@Slf4j
public class BuildRes {
}
|
不需要改动子模块,只需要在自定义插件里面进行修改即可,可以说是超级方便了。
依赖库自动补全
在使用gradle
时,有一点很难受,在依赖库时,没有代码补全,往往每次依赖都要去找,而在maven
中,是有代码补全的。
这里,我们可以使用kotlin DSL
的特性,结合buildSrc
来实现依赖库的伪代码补全,为什么说是伪
呢?因为这个方式不是全自动的。
废话不多说,直接上代码:
首先,在buildSrc
项目下,创建一个Dependencies.kt
文件:
然后,把我们需要依赖的库、版本,分别定义到这个类中:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
object Deps {
val jmh = Jmh
val log = Log
val lib = Lib
object Jmh {
const val plugin = "me.champeau.gradle:jmh-gradle-plugin:0.5.0-rc-2"
const val core = "org.openjdk.jmh:jmh-core:1.23"
}
object Log {
const val commonLog = "commons-logging:commons-logging:1.2"
}
object Lib {
const val json2017 = "org.json:json:20170516"
const val luceneCore = "org.apache.lucene:lucene-core:7.1.0"
const val luceneAnalyzer = "org.apache.lucene:lucene-analyzers-common:7.1.0"
const val mapdb = "org.mapdb:mapdb:3.0.8"
const val log4jApi = "org.apache.logging.log4j:log4j-api:2.11.2"
const val log4jSlf4j = "org.apache.logging.log4j:log4j-slf4j-impl:2.11.2"
}
}
|
gradle
编译后,就可以直接使用啦,在需要依赖时,直接使用:
1
2
|
implementation(Deps.lib.log4jApi)
implementation(Deps.lib.log4jSlf4j)
|
这个时候,因为相当于使用了kotlin
代码,所以,这里是会有代码补全提示的,就可以很愉快地使用啦。
当然,到这里,小伙伴们应该就发现问题了,这也是我为什么说是伪代码补全
的原因,因为这个补全的前提是,我们需要在Deps
类中定义好我们所需要的依赖,这个定义的过程,依旧还是需要我们去找、去查。
尽管如此,还是友好太多了,我们只需要找一次,编写一次,然后在项目中,就可以随意使用、还是带代码补全的,效率提高可不止一点点哈。
模块嵌套
在日常使用中,我们可能会存在模块嵌套,就是多个子模块有共同的父模块,并且有相同的依赖,这时就需要用到模块嵌套了。在gradle
中,模块的嵌套语法其实很简单.
先看我们的项目结构:
1
2
3
4
5
6
7
8
9
10
11
12
|
...
├── provider
│ ├── build.gradle.kts
│ ├── provider-one
│ └── provider-two
├── server
│ ├── HELP.md
│ ├── build
│ ├── build.gradle.kts
│ ├── pom.xml
│ └── src
└── settings.gradle.kts
|
这里我们有一个provider
的模块,下面有有两个子模块provider-one
和provider-two
,他们两个是两个不同的实现,但是有一些相同的依赖。这时我们可以在provider
下定义一个子模块依赖。
编写provider/build.gradle.kts
文件内容:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
// 这里表示当前模块依赖的插件
plugins {
id("com.honlyc.java-conventions")
}
subprojects {
// 这里表示所有子模块所依赖的插件
apply(plugin = "com.honlyc.java-conventions")
// 所有子模块所依赖的库
dependencies {
implementation(Deps.jmh.core)
}
}
// 当前模块名称
description = "provider"
|
这样定义后,该模块下的子模块就很简单了。
编写provider/provider-one/build.gradle.kts
文件内容:
1
2
3
4
5
|
dependencies {
// 内部其他的依赖
}
description = "provider-one"
|
编写provider/provider-two/build.gradle.kts
文件内容:
1
2
3
4
5
|
dependencies {
// 内部其他的依赖
}
description = "provider-two"
|
我们可以发现,这两个子模块并没有再定义plugins
了,因为在夫模块中已经定义好了。这样是不是更为简洁,同时把公共依赖也提取到夫模块中去了。Nice~~
总结
整个转换过程当中,还是遇到很多问题,搞了差不多一天,才完整地弄完,并能够运行。感觉上,gradle
还是比maven
要方便很多,同样,需要学的也变多了。
我主要是苦maven
的version
升级久矣,每次升级打包,就得心跳一番。在gradle
完全就没这个问题,只需要改一个地方,直接打包就好了。
因为也是初学,有说的不对的地方,小伙伴们可以积极指出,我会及时改正。
以上。
参考
https://juejin.cn/post/6844903615346245646
https://medium.com/bumble-tech/how-to-use-composite-builds-as-a-replacement-of-buildsrc-in-gradle-64ff99344b58
https://www.jianshu.com/p/8962d6ba936e
https://blog.csdn.net/weixin_34092455/article/details/89045881