身边有学习安卓的大佬强烈推荐我学习了解下Gradle这款构建工具,说是将会是未来的主流,所以来此学习并记录下。
Ant | Maven | Gradle | |
---|---|---|---|
公司 | Apache | Apache | |
语言 | xml | xml | Groovy |
构建性能 | 速度最快 | 速度最低 | 中间 |
依赖管理 | ivy管理 | GAV管理 | GNV管理 |
插件管理 | 简单 | 复杂 | 简单 |
侧重 | 小型项目构建 | 项目包管理 | 大型项目构建 |
流行程度(2022) | 低 | 高 | 中(未来趋势) |
总结:各有各的优缺点,虽然gradle结合了ant和maven的优点,但是现在的兼容性还比较查
在gradle安转目录的init.d文件夹下新建一个init.gradle文件
init.gradle
allprojects {
repositories {
mavenLocal()
maven {
name "Alibaba";
url "https://maven.aliyun.com/repository/public"
}
maven {
name "Spring";
url "https://repo.spring.io/libs-milestone/"
}
mavenCentral()
}
buildscript {
repositories {
maven {
name "Alibaba";
url 'https://maven.aliyun.com/repository/public'
}
maven {
name "Spring";
url 'https://repo.spring.io/plugins-release/'
}
maven {
name "M2";
url 'https://plugins.gradle.org/m2/'
}
}
}
}
说明(从上至下搜索)
目录 | 功能 |
---|---|
/build | 封装编译后的文件、测试报告 |
/gradle/wrapper/ | 封装包装器文件夹 |
/src | 速度最快| |
/gradlew | mac/linux端包装器启动脚本 |
/gradlew.bat | windows端包装器启动脚本 |
/build.gradle | 构建脚本(类似maven的pom) |
/setting.gradle | 设置项目信息 |
文件结构与maven极其相似,都是基于约定大于配置(Convention over configuration)的设定
dependencies {
//直接依赖
implementation 'org.apache.logging.log4j:log4j:2.17.2'
// 完整写法
implementation group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.8.1'
//依赖当前项目下的某个模块[子工程]
implementation project(':subject01')
//直接依赖本地的某个jar文件
implementation files('libs/foo.jar', 'libs/bar.jar')
//配置某文件夹作为依赖项
implementation fileTree(dir: 'libs', include: ['*.jar'])
}
tips: 若项目中版本号是’+'/'latest.integration’表示下载最新版本, 最好是不用这种动态版本声明,容易出现版本问题
执行gradle build会去配置的依赖仓库下载jar包
类似maven中的scope
类型 | 作用 |
---|---|
compileOnly | 由java插件提供,曾短暂的叫provided,后续版本已经改成了compileOnly,适用于编译期需要而不需要打包的情况 |
runtimeOnly | 由 java 插件提供,只在运行期有效,编译时不需要,比如 mysql 驱动包。,取代老版本中被移除的runtime |
implementation | 由 java 插件提供,针对源码[src/main 目录] ,在编译、运行时都有效,取代老版本中被移除的 compile |
testCompileOnly | 由 java 插件提供,用于编译测试的依赖项,运行时不需要 |
testRuntimeOnly | 由 java 插件提供,只在测试运行时需要,而不是在测试编译时需要,取代老版本中被移除的 testRuntime |
testImplementation | 由 java 插件提供,针对测试代码[src/test 目录] 取代老版本中被移除的 testCompile |
providedCompile | war 插件提供支持,编译、测试阶段代码需要依赖此类 jar 包,而运行阶段容器已经提供了相应的支持,所以无需将这些文件打入到 war 包中了;例如 servlet-api.jar、jsp-api.jar |
编译范围依赖在所有的 classpath 中可用,同时它们也会被打包。在 gradle 7.0 已经移除 | |
runtime 依赖在运行和测试系统的时候需要,在编译的时候不需要,比如 mysql 驱动包。在 gradle 7.0 已经移除 | |
api | java-library 插件提供支持,这些依赖项可以传递性地导出给使用者,用于编译时和运行时。取代老版本中被移除的 compile |
compileOnlyApi | java-library 插件提供支持,在声明模块和使用者在编译时需要的依赖项,但在运行时不需要。 |
官方:
各个依赖范围的关系和说明
依赖范围升级和移除
API和implemention区别
执行 java 命令时都使用了哪些依赖范围的依赖
tips:
依赖冲突是指 “在编译过程中, 如果存在某个依赖的多个版本, 构建系统应该选择哪个进行构建的问题”
implementation('org.hibernate:hibernate-core:3.6.3.Final'){
//下面三种都行
exclude group: 'org.slf4j'
//exclude module: 'sl4j-api'
//exclude group: 'org.slf4j', module: 'sl4j-api'
}
implementation('org.hibernate:hibernate-core:3.6.3.Final'){
//不允许依赖传递,一般不用
transitive(false)
}
//强制使用某个版本!!【官方建议使用这种方式】
implementation('org.slf4j:slf4j-api:1.4.0!!')
//这种效果和上面那种一样,强制指定某个版本
implementation('org.slf4j:slf4j-api:1.4.0'){
version{
strictly("1.4.0")
}
}
官方文档
命令 | 作用 |
---|---|
gradle build | 构建项目:编译、测试、打包 |
gradle run | 运行一个自定义服务,需要application插件支持,并且指定了主启动类(mainClassName=xxx)才能运行 |
gradle clean | 清空build目录 |
gradle init | 初始化gradle项目使用 |
gradle wrapper | 生成wrapper文件夹 |
gradle wrapper --gradle-version=x.x | 升级 wrapper 版本号 |
gradle wrapper --gradle-version 5.2.1 --distribution-type all | 关联源码用 |
gradle classes | 编译业务代码和配置文件 |
gradle test | 编译测试代码,生成测试报告| |
gradle build -x test | 跳过测试构建 |
gradle [taskName] :执行自定义任务 |
命令 | 作用 |
---|---|
gradle projects | 列出所选项目及子项目列表,以层次结构的形式显示 |
gradle tasks | 列出所选项目【当前 project,不包含父、子】的已分配给任务组的那些任务 |
gradle tasks --all | 列出所选项目的所有任务 |
gradle tasks --group=“build setup” | 列出所选项目中指定分组中的任务 |
gradle help --task someTask | 显示某个任务的详细信息 |
gradle dependencies | 查看整个项目的依赖信息,以依赖树的方式显示 |
gradle properties | 列出所选项目的属性列表 |
选项 | 作用 |
---|---|
-h,–help | 查看帮助信息 |
-v, --version | 打印 Gradle、 Groovy、 Ant、 JVM 和操作系统版本信息 |
-S, --full-stacktrace | 打印出所有异常的完整(非常详细)堆栈跟踪信息 |
-s,–stacktrace | 打印出用户异常的堆栈跟踪(例如编译错误) |
-Dorg.gradle.daemon.debug=true | 调试 Gradle 守护进程 |
-Dorg.gradle.debug=true | 调试 Gradle 客户端(非 daemon)进程 |
-Dorg.gradle.debug.port=(port number) | 指定启用调试时要侦听的端口号。默认值为 5005 |
tips: 在gradle.properties中指定这些选项中的许多选项,因此不需要命令行标志
选项 | 作用 |
---|---|
–build-cache, --no-build-cache | 尝试重用先前版本的输出。默认关闭(off) |
–max-workers | 设置 Gradle 可以使用的 woker 数。默认值是处理器数 |
-parallel, --no-parallel | 并行执行项目。有关此选项的限制,请参阅并行项目执行 |
选项 | 作用 |
---|---|
–daemon, --no-daemon | 使用 Gradle 守护进程运行构建。默认是 on |
–foreground | 在前台进程中启动 Gradle 守护进程。 |
-Dorg.gradle.daemon.idletimeout=(number of milliseconds) | Gradle Daemon 将在这个空闲时间的毫秒数之后停止自己。默认值为 10800000(3 小时)。默认设置为关闭(off) |
选项 | 作用 |
---|---|
-Dorg.gradle.logging.level=(quiet,warn,lifecycle,info,debug) | 通过 Gradle 属性设置日志记录级别 |
-q, --quiet | 只能记录错误信息 |
-w, --warn | 设置日志级别为warn |
-i, --info | 将日志级别设置为info |
-d, --debug | 登录调试模式(包括正常的堆栈跟踪) |
选项 | 作用 |
---|---|
-x | -x 等价于: --exclude-task : 常见 gradle -x test clean build |
–rerun-tasks | 强制执行任务,忽略 up-to-date ,常见 gradle build --rerun-tasks |
–continue | 忽略前面失败的任务,继续执行,而不是在遇到第一个失败时立即停止执行。每个遇到的故障都将在构建结束时报告,常见:gradle build --continue。 |
gradle init --type pom | 将 maven 项目转换为 gradle 项目(根目录执行) |
GradlerWrapper为解决没有gradle的环境下使用gradle项目的问题。只需要使用构建项目时生成的gradlew/gradlew.bat,需注意的是使用gradlew的命令需要依赖于jdk
参数 | 说明 |
---|---|
–gradle-version | 用于指定使用的Gradle版本 |
–gradle-distribution-url | 用于指定下载Gradle发行的url地址 |
test {
enabled(true) // build时执行同时进行测试,生成测试报告
useJUnitPlatform() //支持Junit5测试
// include("space/rexhub/bj/test/*") // 测试指定包
exclude("space/rexhub/bj/test/*") // 不测试指定包
}
项目实质上是Task 对象的集合。一个 Task 表示一个逻辑上较为独立的执行过程,比如编译 Java 源代码,拷贝文件,打包 Jar 文件,甚至可以是执行一个系统命令。另外,一个 Task 可以读取和设置 Project 的 Property 以完成特定的操作。
def map = Collections.singletonMap("action", {println "task one..."})
//task(map, "task1", {})
task (map, "task1") {
// 任务的配置段,在任务的配置阶段进行
println "这是最简单的任务"
// 任务的行为:在任务执行阶段执行
doFirst{
println "task1 do first"
}
//
doLast{
println "task1 do last"
}
}
task1.doFirst {
println "task1 do first(outer)"
}
task1.doLast {
println "task1 do first(outer)"
}
> Configure
....
这是最简单的任务
....
> Task :task1
....
task1 do first(outer)
task1 do first
task one...
task1 do last
task1 do first(outer)
属性 | 描述 | 默认值 |
---|---|---|
type | 基于一个存在的Task来创建,类似继承 | DefaultTask |
overwrite | 是否替换存在的Task,这个和type配合起来用 | false |
dependsOn | 配置任务的依赖 | [] |
action | 添加到任务中的一个Action或者一个闭包 | null |
description | 配置任务的描述 | null |
group | 配置任务的分组 | null |
enable | 任务是否开启 | true |
timeout | 任务执行的时间 | (tasks never time out) |
onlyIf | 任务断言, 如:myClean.onlyIf(!project.hasProperties(“clean”)),执行:gradle myClean -Pclean 才会调用myClean的方法 | null |
方法 | 描述 | 用例 |
---|---|---|
findByName | 根据任务名查找 | tasks.findByName(“myClean”).doFirst({…}) |
findByPath | 根据任务路径查找 | tasks.findByName(“:myClean”).doFirst({…}) |
addRule | 处理任务不存在异常 | tasks.addRule(“cleanRule”){String taskName -> task(taskName){println “任务不存在”} } |
无论是定义任务自身的 action,还是添加的 doLast、doFirst 方法,其实底层都被放入到一个 Action 的 List中了,最初这个 action List 是空的,当我们设置了 action【任务自身的行为】,它先将 action 添加到列表中,此时列表中只有一个 action,后续执行 doFirst 的时候 doFirst 在 action 前面添加,执行 doLast 的时候 doLast 在 action 后面添加。doFirst永远添加在 actions List 的第一位,保证添加的 Action 在现有的 action List 元素的最前面;doLast 永远都是在 action List末尾添加,保证其添加的 Action 在现有的 action List 元素的最后面。一个往前面添加,一个往后面添加,最后这个 actionList 就按顺序形成了 doFirst、doSelf、doLast 三部分的 Actions,就达到 doFirst、doSelf、doLast 三部分的 Actions 顺序执行的目的
参数方式依赖
实例:
task A {
doLast{
println "TaskA..."
}
}
task B {
doLast{
print "TaskB..."
}
}
// 参数方式依赖:dependsOn后面用冒号
task 'C' (dependsOn: ['A', 'B']){
doLast {
println "TaskC..."
}
}
输出:
> Task :A
TaskA...
> Task :B
TaskB...
> Task :C
TaskC...
内部依赖
实例:
task 'A'{...}
task 'B'{...}
// 参数方式依赖
task 'C'{
// 内部依赖:dependsOn后面=号
dependsOn=['A', 'B']
doLast {
println "TaskC."
}
}
输出同上
外部依赖
实例:
// 外部依赖:可变参数可加可不加
task 'A'{...}
task 'B'{...}
// 参数方式依赖
task 'C'{...}
C dependsOn('B', 'A')
输出同上
tips:
常见任务类型 | 该类型任务的作用 |
---|---|
Delete | 删除文件或目录 |
Copy | 将文件复制到目标目录中。此任务还可以在复制时重命名和筛选文件 |
CreateStartScripts | 创建启动脚本 |
Exec | 执行命令行进程 |
GenerateMavenPom | 生成 Maven 模块描述符(POM)文件。 |
GradleBuild | 执行 Gradle 构建 |
Jar | 组装 JAR 归档文件 |
JavaCompile | 编译 Java 源文件 |
Javadoc | 为 Java 类生成 HTML API 文档 |
PublishToMavenRepository | 将 MavenPublication 发布到 mavenartifactrepostal |
Tar | 组装 TAR 存档文件 |
Test | 执行 JUnit (3.8.x、4.x 或 5.x)或 TestNG 测试 |
Upload | 将 Configuration 的构件上传到一组存储库 |
War | 组装 WAR 档案 |
Zip | 组装 ZIP 归档文件。默认是压缩 ZIP 的内容 |
继承DefaultTask
class CustomTask extends DefaultTask {
//@TaskAction表示Task本身要执行的方法
@TaskAction
def doSelf(){
println "Task自身在执行的in doSelf"
}
}
def myTask=task MyDefinitionTask (type: CustomTask)
myTask.doFirst(){
println "task 执行之前 执行的 doFirst方法"
}
myTask.doLast(){
println "task 执行之后 执行的 doLast方法"
}
4.times { counter ->
tasks.register("task$counter") {
doLast {
println "I'm task number $counter"
}
}
}
tasks.named('task0') { dependsOn('task2', 'task3') }
ext{
company = "Rexhub"
cfgs = ["compileSdkVersion":"JavaVersion.VERSION_1_S"]
spring = [
version: '5.0.0'
]
}
task versionTask{
doLast{
println "公司名称为:${company},JDK版本是${cfgs.compileSdkVersion},版本号是${spring.version}"
}
}
执行脚本结果:
> Task :versionTask
公司名称为:Rexhub,JDK版本是JavaVersion.VERSION_1_S,版本号是5.0.0
tips: 脚本文件可以是本地的,也可以是网络上的
二进制插件(对象插件)就是实现了 org.gradle.api.Plugin 接口的插件,每个 Java Gradle 插件都有一个 plugin id。核心插件(gradle官方)
//plugin: '插件id/插件全类名/插件简类名'
apply plugin: 'java' //map具名参数方式
apply{
//plugin '插件id/插件全类名/插件简类名'
plugin 'java'
}
plugins {
id 'java'
id 'application'
}
buildscript {
ext {
springBootVersion = "2.3.3.RELEASE"
}
repositories {
mavenLocal()
maven { url 'http://maven.aliyun.com/nexus/content/groups/public' }
jcenter()
}
// 此处先引入插件
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
tips:
apply plugin: 'org.springframework.boot' //社区插件,需要事先引入,不必写版本号
plugins {
id 'org.springframework.boot' version '2.4.1'
}
官方文档
interface GreetingPluginExtension {
Property<String> getMessage()
Property<String> getGreeter()
}
class GreetingPlugin implements Plugin<Project> {
void apply(Project project) {
def extension = project.extensions.create('greeting', GreetingPluginExtension)
project.task('hello') {
doLast {
println "${extension.message.get()} from ${extension.greeter.get()}"
}
}
}
}
apply plugin: GreetingPlugin
// Configure the extension using a DSL block
greeting {
message = 'Hi'
greeter = 'Gradle'
}
buildSrc 是 Gradle 默认的插件目录,编译 Gradle 的时候会自动识别这个目录,将其中的代码编译为插件
apply plugin: 'groovy' //必须
apply plugin: 'maven-publish'
dependencies {
implementation gradleApi() //必须
implementation localGroovy() //必须
}
repositories {
google()
jcenter()
mavenCentral() //必须
}
//把项目入口设置为src/main/groovy
sourceSets {
main {
groovy {
srcDir 'src/main/groovy'
}
}
}
package space.rexhub
import org.gradle.api.Plugin
import org.gradle.api.Project
/**
* Description: Text插件
* @author Rex
* @date 2022-11-06 15:26
*/
class Text implements Plugin<Project>{
@Override
void apply(Project project) {
project.task("rexhub"){
doLast {
println "自定义插件使用"
}
}
}
}
implementation-class=space.rexhub.Text
tips: 这种方式可以跨模块调用,但仅限于本工程
基于上述步骤改进
apply plugin: 'groovy' //必须
apply plugin: 'maven-publish'
dependencies {
implementation gradleApi() //必须
implementation localGroovy() //必须
}
repositories {
google()
jcenter()
mavenCentral() //必须
}
//把项目入口设置为src/main/groovy
sourceSets {
main {
groovy {
srcDir 'src/main/groovy'
}
}
}
publishing {
publications {
myLibrary(MavenPublication) {
groupId = 'space.rexhub.plugin' //指定GAV坐标信息
artifactId = 'library'
version = '1.1'
from components.java//发布jar包
//from components.web///引入war插件,发布war包
}
}
repositories {
maven { url "$rootDir/lib/release" }
//发布项目到私服中
// maven {
// name = 'myRepo' //name属性可选,表示仓库名称,url必填
// //发布地址:可以是本地仓库或者maven私服
// //url = layout.buildDirectory.dir("repo")
// //url='http://my.org/repo'
// // change URLs to point to your repos, e.g. http://my.org/repo
// //认证信息:用户名和密码
// credentials {
// username = 'joe'
// password = 'secret'
// }
// }
}
}
buildscript {
repositories {
maven { url "$rootDir/lib/release" }
}
dependencies {
classpath "space.rexhub.plugin:library:1.1"
}
}
plugins{
id 'java'
id 'space.rexhub.plugin'
}