Gradle

发布时间 2023-09-13 20:26:35作者: Bingmous

官网:https://gradle.org/

概述

maven侧重jar包管理,gradle侧重项目的构建,构建性能远高于maven,特别是大型项目

一些开源框架已经由maven转向给了gradle,如spring

常见构建工具:

  • Ant:2000年Apache退出的纯java编写的构建工具。
    • 优点:使用灵活,速度快(快与maven和gradle)。
    • 缺点是没有强加任何编码约定的项目目录,需要编写繁杂的xml文件构建指令
  • Maven:2004年apache推出的使用xml和pom管理项目的构建工具
    • 优点:遵循一套约定大于配置的项目目录结构,统一使用gav坐标管理依赖,侧重与包管理
    • 缺点:项目构建过程过于僵化,配置文件编写不够灵活、不方便自定义组件,构建速度慢与gradle
  • Gradle:2012年Google推出的基于Groovy语言的全新项目构建工具,集合了Ant和Maven各自的优势
    • 优点:集Ant脚本的灵活性+Maven约定大于配置的项目目录优势,支持多种远程仓库,侧重于大型项目构建
    • 缺点:学习成本高、资料少、脚本灵活、版本兼容性差

安装

  • 下载:https://gradle.org/releases/ ,下载complete包,包含文档和源码
  • 解压到指定目录,配置GRADLE_HOME,并配置bin目录到path
  • gradle -v检查是否安装成功,需要先安装jdk
  • 配置GRADLE_USER_HOME为maven仓库地址,共用一个仓库

目录

  • build,编译后的字节码、打成的包
  • gradle
  • src
    • main
      • java
      • resources
    • test
      • java
      • resources
  • gradlew
  • gradlew.bat
  • buid.gradle,构建脚本,每个项目一个
  • settings.gradle,设置文件,定义项目及子项目名称

Wrapper包装器

实际上是对gradle的一层包装,用于解决实际开发过程中可能遇到的不同项目需要不同版本的gradle问题。如果电脑上没有安装gradle或者版本太旧,这时候可以考虑使用gradle wrapper。这也是官方建议的原因,实际上有了gradle wrapper之后,本地是可以不配置gradle的。借助gradlew、gradlew.cmd脚本,它调用的就是工程中wrapper中的jar包

升级gradlew中的版本:gradle wrapper --gradle-version=8.3,只是修改properties中的版本号,自己直接改应该也可以吧

gradle wrapper执行流程:

  • 第一次执行时会进行下载,下载到properties配置的目录下,zipStoreBase=GRADLE_USER_HOMEzipStorePath=wrapper/dists
  • 并进行解压,解压到distributionBase=GRADLE_USER_HOMEdistributionPath=wrapper/dists目录下
  • 并在GRADULE_USER_HOME环境变量下的cache目录里进行缓存
  • distributionUrl,发行版压缩包下载地址
  • 如果没有配置环境变量,则默认在当前目录下的.gradle目录下

新建一个项目时,使用本地的gradle即可,下载第三方的项目或旧项目使用gradlew

修改maven下载源

在gradle安装目录init.d目录下创建init.gradle(.gradle文件都会执行)

  • mavenLocal()需要配置maven安装目录的环境变量M2_HOME
  • gradle下载的依赖会放在GRADULE_USER_HOME环境变量下的cache目录里
allprojects {
    repositories {
        mavenLocal()
		maven{name "Alibaba"; url "https://maven.aliyun.com/repository/public"}
		maven{name "Bstek"; url "https://nexus.bsdn.org/content/groups/public"}
		mavenCentral()
    }

	buildscript {
		repositories {
			maven{name "Alibaba"; url "https://maven.aliyun.com/repository/public"}
			maven{name "Bstek"; url "https://nexus.bsdn.org/content/groups/public"}
			maven{name "M2"; url "https://plugins.gradle.org/m2"}
		}
	}
}

Gradle常用命令

gradle的指令要在含有build.gradle的目录执行

  • gradle clean,清空build目录
  • gradle classes,编译业务代码和配置文件
  • gradle test,编译测试代码,生成测试报告
  • gradle build,构建项目,会执行前面的classes、test
  • gradle build -x test,跳过测试构建项目

Groovy简介

官网:http://www.groovy-lang.org/

在某种程度上,Groovy可以被视为Java的一种脚本化改良版,Groovy也是运行在JVM上,可以很好的与Java代码及相关库进行交互操作,它是一种成熟的面向对象编程语言,既可以面向对象编程,又可以用作纯粹的脚本语言。大多数有效的Java代码也可以转换为有效的Groovy代码,和Java的主要区别是,完成同样的任务所需要的的Groovy代码比Java代码更少。

特点:

  • 功能强大,例如提供了动态类型转换,闭包和元编程支持
  • 支持函数式编程,不需要main函数
  • 默认导入常用的包
  • 类不支持default作用域,默认public
  • Groovy中基本类型也是对象,可以直接调用对象方法
  • 支持DSL和其他简介的语法,让代码易于阅读和维护
  • Groovy是基于Java语言的,所以完全兼容Java语法,所以对Java程序员的学习成本较低。

安装

下载后解压,配置到GROOVY_HOME,将bin目录配置到path,groovy -v检查是否安装成功

使用

参考官网

  • 字符串
  • 数据类型
  • 权限修饰符
  • 集合操作
  • 类导入
  • 异常处理
  • 闭包

IDEA创建Gradle项目

选择本地安装的gradle,idea中gradle刷新和terminal中gradlew使用的源可能不一样

创建普通java工程

创建项目时可以选择本地gradle(不会自动生成gradle/wrapper目录了),也可以选择wrapper形式(idea2023.1.4支持到gradle8.0)

创建完项目后可以在File | Settings | Build, Execution, Deployment | Build Tools | Gradle中手动调整为使用本地gradle

创建web工程

  • 调整plugins中id,添加id war
  • 添加依赖
  • 在main下添加webapp目录

gretty部署项目

  • 将java项目打成war包之后,就需要部署到服务器运行,
    • 部署到本地tomcat
    • 使用gretty插件中内置服务器方式部署项目

gretty是一个功能丰富的gradle插件,用于在嵌入的servlet容器上运行web应用程序,让项目开发和部署更加简单,目前gretty插件已经作为gradle的核心库使用了,其核心功能为支持jetty、tomcat等sevlet容器,支持项目热部署、https、调试

gretty官网:https://akhikhl.github.io/gretty-doc/index.html

使用:

  • 引入gretty插件,id 'org.gretty' version '2.2.0'
  • 指定maven仓库,jcenter()mavenCentral()
  • 对插件进行配置
  • 执行gretty插件,gradle appRun

Gradle对测试的支持

测试任务自动检测并执行所有单元测试,最终生成一个报告,支持junit和testNG

使用:

  • 导入依赖
  • 如果使用junit5,需要使用useJUnitPlatform(),也可以进行其他配置
  • 写单元测试
  • 执行build

Gradle高级

项目的生命周期

Initialization -> Configuration -> Execution
image

在最后的execution阶段执行由task组成的有向无环树

settings文件

  • 作用:主要是在仙姑初始化阶段确定引入哪些工程,为构建项目工程树做准备
  • 工程树:类似于maven中的project和module
  • 定义了当前gradle项目及子项目的项目名称
  • 必须放在根工程目录下,名字为settings.gradle不能修改
  • 对org.gradle.api.initialization.Settings实例是对应关系,每个项目只有一个settings文件
  • 作为开发者只需要关注include方法即可,使用相对路径:引入子工程,一个子工程只有在settings文件中配置了才能被gradle识别,在构建的时候才会包含进去
  • 项目名称中:代码项目的分隔符,类似路径中的/,以:开头表示相对于root project,gradle会为每个带有build.gradle脚本文件的工程构建一个Project对象

Task

项目实质上是Task对象的集合,一个Task表示一个逻辑上较为独立的执行过程,比如编译Java源码、拷贝文件、打包jar文件,甚至是执行一个系统命令。一个Task也可以读取和设置Project的Property以完成特定的操作。

//task: gradle -i task1进行执行 先进行配置阶段、再进行执行阶段
//在不引起歧义的情况下 方法小括号可以省略 参数的引号也可以省略
task task1 { //闭包作为最后一个参数可以写在外面
    //任务的配置段 在项目生命周期的配置阶段执行
    println "task1 configuration"

    //任务的行为 在项目的执行阶段执行 doFirst会在doLast之前执行
    doFirst {
        println "task1 doFirst"
    }

    doLast {
        println "task1 doLast"
    }
}

任务的行为
行为也可以在任务外定义,在执行时,先执行doFirst out,再执行doFirst,再执行doLast,再执行doLast out

task1.doFirst {
    println "task1 doFirst out"
}

task1.doLast {
    println "task1 doLast out"
}

定义task时也可以传入map参数,定义任务自身的action,map中放入action作为key,闭包作为value,那么这个闭包会在doFirst和doLast之间执行

原理:action实际上是一个列表,每次添加一个doFirst都会添加到列表的开头,每次添加一个doLast都会添加到列表的结尾,

任务的依赖方式
task之间的依赖关系可以在以下几部分进行设置,1、参数依赖,2、内部依赖,3、外部依赖

  • 参数依赖:在参数上指明依赖的任务列表,使用dependsOn: ['task1', 'task2']
  • 内部依赖:在task内部,为dependsOn属性赋值,dependsOn = ['task1', 'task2']
  • 外部依赖:在任务外动态赋值dependsOn属性,task5.dependsOn = ['task1', 'task2']

也可以跨项目依赖任务,在写依赖时使用相对目录[":gradle-sub1:sub1_task1"]

任务执行
执行任务:gradle task_name,或在idea的gradle窗口双击,详细命令参考官网

  • 常见的任务
    • gradle build,构建项目:编译、测试、打包等操作
    • gradle run,运行一个服务,需要添加application插件,并指定主启动类
    • gradle clean,用于清理当前项目下的build目录
    • gradle init,初始化gradle项目使用
    • gradle wrapper,生成wrapper文件夹
      • 升级gradle版本号:gradle wrapper --gradle-version=x.x
      • 关联源码:gradle wrapper --gradle-version x.x --distribution-type all
  • 项目报告相关任务
    • gradle project,列出所选项目及子项目列表,以层次结构显示
    • gradle tasks,列出当前项目的已分配给任务组的task
      • gradle tasks --all,列出所选项目的所有任务
      • gradle tasks --group="group_name",列出所选项目指定分组的任务
      • gradle help --task task_name,显示某个任务的详细信息
      • 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客户端
    • -Dorg.gradle.debug.port=5005,指定调试时要监听的端口号,默认5005
  • 性能相关,可以在gradle.properties中指定这些选项中的许多选项,可以不用命令行
    • --build-cache, --no-build-cache,尝试重用之前版本的输出,默认关闭off
    • --max-workers,设置gradle可以使用的worker数,默认是处理器数
    • --parallel, --no-parallel,并行执行,关于此选项的限制,参考相关文档,默认关闭off
  • 守护进程选项
    • -daemon, no-daemon,使用gradle守护进程进行构建,默认是on
    • --foreground,在前台进程中启动gradle进程
    • -Dorg.gradle.daemon.idletimeout=xxx(ms),默认3小时,在这个时间后停止自己
    • gradle --stop,停止守护进程
  • 日志选项
    • -Dorg.gradle.logging.level=(quiet/warn/lifecycle/info/debug),通过gradle属性设置日志级别
    • -q, --quiet,只记录错误信息
    • -w, --warn,设置日志级别为warn
    • -i, --info,设置日志级别为info
    • -d, --debug,调试模式,包括正常的堆栈跟踪
  • 其他
    • -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项目(根目录执行)
    • gradle task_name,执行自定义任务

其他:

  • 任务名支持缩写,如connectTask,可以使用gradle cT,使用每个单词的首字母
  • gradle指令的本质是一个个task,gradle的所有操作都是基于task完成的
    image

任务定义方式
一是通过Project的task方法,另一种则是通过tasks对象的create()或register()方法,通过register创建是延迟创建,只有当task被需要使用的时候才会被创建。

在定义任务时也可以指定任务的属性:
image

任务类型
默认定义的task都是DefaultTask类型的,gradle提供了现有的任务类型帮助我们快速完成想要的任务,只需要在创建任务的时候指定当前任务的任务类型即可,然后就可以使用这种类型中的属性和方法了。可参考官网

也可以自定义类型,继承DefaultTask
image

任务的执行顺序
gradle中有三种方式可以指定task的执行顺序:

  • dependsOn强依赖方式
  • 通过task输入输出
  • api指定

参考官网

动态分配任务
gradle的强大不仅仅用于定义任务功能,可以使用它在循环中注册同一类型的多个任务,如:

//动态分配任务
4.times {
    tasks.register("timesTask$it") {
        println "timesTask[${name}] configuration"
        doFirst {
            println "I'm timesTask[${name}]"
        }
    }
}
//使用api强制timesTask0依赖于某些任务
tasks.named('timesTask0') { dependsOn('task1', 'task2')}

任务的关闭和重启
修改task的enabled属性,默认为true

任务的超时
修改timeout属性,参数为Duration,超时会抛出异常,后续任务不会再执行,可以使用--continue在某些任务失败后继续执行其他任务

任务的查找
可以通过路径或名称查找任务

任务的规则
当执行、依赖一个不存在的任务时,gradle会执行失败,可以通过添加规则让其继续执行。还可以根据不同的规则创建需要的任务。

任务的onlyIf断言
task有一个onlyIf方法,它接收一个闭包参数,如果该闭包返回true就执行任务,否则跳过。这很有用,比如控制程序什么情况下打什么包,什么时候执行单元测试,什么时候执行单元测试不执行网络测试等。

默认任务
将任务放入方法defaultTasks中,执行gradle就会默认执行

Gradle中的文件操作

几种常见的文件操作方式:本地文件、文件集合、文件树、文件拷贝、归档文件

本地文件
使用Project.file()方法(也可以自己new File),通过指定文件的相对路径或绝对路径来对文件操作,相对路径为相对于当前项目的或子项目的目录,返回的就是File对象,可以使用它就像在java中使用一样。

文件集合
使用Project.files()方法,返回的文件集合可以进行加减

文件树
使用Project.fileTree()方法,文件树是有层次结构的文件集合,一个文件树可以代表一个目录结构或一个zip压缩包中的内容结构。文件树是从文件集合继承过来的,所以文件树具有文件集合所有的功能。

文件拷贝
使用Copy这个类型的task,或者使用Project的copy方法

归档文件
通常一个项目有很多的jar包,我们希望把项目打包成一个war/zip/tar包进行发布,这是我们就用War/Zip/Tar/Ear任务来实现。读取压缩包使用Project.xxxTree()

Gradle中的Dependencies

  • 本地依赖:依赖于本地的jar包,具体可通过文件集合、文件树的方式指定
  • 项目依赖:依赖于别的project
  • 直接依赖:依赖的类型、组名、名称、版本号

依赖下载
使用配置maven仓库地址下载

依赖的类型
详情参考官网
常见的依赖类型:

  • compileOnly:编译时需要,打包时不需要,如sevlet-api,类似maven的provided
  • runtimeObly:编译时不需要,运行时需要,比如mysql驱动包,取代老版本的runtime
  • implementation:编译运行时都需要,取代老版本compile
  • testCompileOnly:针对测试的,编译测试时需要,运行时不需要
  • testRuntimeOnly:针对测试的,测试运行时需要,编译时不需要,取代老版本的testRuntime
  • testImplementation:针对测试的,编译运行都需要,取代老版本的testCompile

注意点:

  • Implementation不支持依赖传递,api支持依赖传递,适用于工程有多个模块场景下为了避免重复依赖(引入第三方的会传递依赖),可以使用api,如a依赖于b,b依赖于c,则a implementation b则无法会用模块c的,必须导入模块c,可以使用a api b,那么就可以使用模块c了。
  • 如果使用api进行依赖传递,则当底层模块变化时,所有上层模块都要重新编译,速度没有implementation快
    image

依赖冲突及解决方案
gradle默认使用高版本依赖,也可以排除依赖、强制使用某个版本(在版本号后面加两个!,或者使用闭包的形式)、取消依赖传递(只保留模块自己,模块的依赖都排除掉)

可以配置依赖冲突是直接失败

configurations.all {
    Configuration configuration ->
        //当版本冲突时直接构建失败
        configuration.resolutionStrategy.failOnVersionConflict()
}

如果版本中写的是+或latest.integration,表示使用最新版本,会遍历所有仓库,找到最新的版本。(不建议使用)

Gradle中的插件

官网文档:https://docs.gradle.org/current/userguide/plugin_reference.html
image

脚本插件
本质就是一个脚本文件,在build.gradle中引入即可

对象插件
实现了org.gradle.api.Plugin接口的插件,每个java gradle插件都有一个plugin id
image

内部插件(核心插件):可参考官网

第三方插件:

  • 一般需要配置对应的仓库和类路径,buildscript标签要在所以标签之前
  • 如果使用传统方式,插件必须已经被托管到了https://plugins.gradle.org 上,就可以不用在buidscript里配置classpath依赖了

用户自定义插件
实现Plugin接口,泛型为Project,参考官网。只能当前项目使用,其他项目不能使用,局限性强,一般不使用

buildSrc项目
buildSrc是gradle默认的插件目录,编译gradle的时候会自动识别这个目录,将其中的代码编译为插件

创建buildSrc项目,只保留build.gradle和src/main目录,那么这里定义的插件可以被所有工程使用,但是不能被其他工程使用,如果需要别的工程也可以使用,可以单独定义一个插件模块发布到仓库,使用buildScript引入(要放在其他标签之前)

插件的关注点

  • 使用:apply plugin: '插件名'
  • 主要的功能,以及插件的添加的任务有哪些:可以通过gradle tasks查看添加了哪些任务
  • 项目目录结构
  • 依赖管理
  • 插件常用的属性

build.gradle文件

  • 是一个脚本,支持java/groovy等语言
  • 每个project都会有一个build.gradle文件,是项目构建的入口
  • 每个build.gradle文件都对应一个Project实例,对文件的设置本质上就是配置Project实例的属性和方法
  • root project可以获取到所有的child project,所以可以在root工程的build.gradle文件中对child project做一些统一的配置,如依赖的maven仓库、插件等
  • build.gradle常见的属性:
    image

ext定义的属性也可以在任务中、子project中使用

publishing项目发布

  • 需要引入maven-publish插件
  • 设置相关的发布配置:gav、仓库信息
  • 执行相关的发布指令
    如果需要发布war包,需要引入war插件,如果需要携带源码和javadoc的发布,需要java-library插件(java插件的升级版)

生命周期中的Hook

是由gradle自动回调完成的

  • Initialization阶段
    • 先加载初始化脚本
    • 执行当前工程的setting.gradle文件
    • 执行钩子函数gradle.settingsEvaluated
    • 根据当前项目及子项目名字构建Project实例
    • 执行钩子函数gradle.projectsLoaded
  • 配置阶段
    • 执行当前工程钩子函数project.beforeEvaluate/gradle.beforeProject
    • 执行当前工程build.gradle及其中声明的task的配置段
    • 执行当前工程钩子函数project.afterEvaluate/gradle.afterProject
    • 再执行各个子工程的...
    • 所有项目执行完之后,执行钩子函数gradle.projectsEvaluated
    • task依赖关系图建立完毕后会执行钩子函数gradle.taskGraph.whenReady
  • 执行阶段
    • 根据有向无环图依次执行每个task,其中:
    • 先执行钩子函数gradle.taskGraph.beforeTask
    • 再执行task中的before actions
    • 再执行task中的after actions
    • 再执行钩子函数gradle.taskGraph.afterTask
    • 所以task执行完毕后,再执行钩子函数gradle.buildFinished

gradle脚本文件执行过程中会生成对应的实例,主要有以下几种对象:

  • Gradle对象,在项目初始化时构建,全局单例存在,只有一个对象实例
  • Project对象,每一个build.gradle文件都会转换为一个Project对象实例,类似于maven项目中的pom文件
  • Settings对象,setting.gradle会转换为一个settings对象,和整个项目是一对一的关系,一般只是用include方法包含子工程
  • Task对象,每个build.gradle中可以定义task,gradle是基于task的,一个项目可以有一个或多个task

创建springboot项目

getting started:https://docs.spring.io/spring-boot/docs/current/reference/html/getting-started.html#getting-started
springboot-gradle插件:https://docs.spring.io/spring-boot/docs/3.1.3/gradle-plugin/reference/htmlsingle/#introduction

  • 导入插件
  • 导入依赖