1. 项目结构
目录介绍:
- build.gradle:项目编译时要读取的配置文件,build.gradle有两个,一个是全局的,一个是在模块里面。全局的build.gradle主要设置的是声明仓库源,gradle的版本号说明等。
- gradlew:linux下的gradle环境脚本。可以执行gradle指令,比如./greadle build。
- gradlew.bat:windows下的gradle环境脚本。可以执行gradle指令。
- settings.gradle:包含一些必要设置,例如,任务或项目之间的依赖关系等,无论有多少子模块,该文件都只有一个,且在根目录中。
- gradle:包含wrapper文件夹及其两个子文件,可以自动安装gradle环境。也就是如果本地没有安装gradle环境,则可以使用这里的。
Gradle-Wrapper是为了简化Gradle的安装和部署,目的是为了使任意的gradle项目都不需要单独安装环境,项目会自动识别有无gradle环境。如果在本地没有找到与gradle-wrapper.properties中版本相同的Gradle。IDEA就会自动帮你下载一个gradle环境。gradle/wrapper/gradle-wrapper.properties内容解析:
1#greadistributionBase和distributionPath配合使用,指定gradle解压后的存放位置。 2#GRADLE_USER_HOME表示用户目录,可以在环境变量中配置,如果未配置,则使用默认的: 3 #windows系统默认是:C:\window\<user-name>\.gradle\ 4 #linux系统:$HOME/.gradle 5greadistributionBase=GRADLE_USER_HOME 6distributionPath=wrapper/dists 7 8#指定某个版本的gradle的下载地址。 9distributionUrl=https://services.gradle.org/distributions/gradle-9.2.1-bin.zip 10networkTimeout=10000 11validateDistributionUrl=true 12 13#zipStoreBase和zipStorePath配合使用,指定下载的gradle.zip文件的存放位置。 14zipStoreBase=GRADLE_USER_HOME 15zipStorePath=wrapper/dists 16
这里的 包装器 指的就是使用gradle-wrapper.properties指定的gradle来构建项目。
Gradle遵循COC(约定优于配置)的理念,默认情况下提供了与Maven项目相同的项目结构配置:
1project root 2src/main/java 3src/main/resource 4src/test/java 5src/test/resource 6src/main/webapp(web工程) 7
2. 常用命令
执行 gradle build 构建项目,会产生一个build目录,里面有打包好的jar包。
执行 gradle build -x test 跳过测试构建项目。
1PS D:\Project\gradle-demo> gradle build 2Java HotSpot(TM) 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended 3 4BUILD SUCCESSFUL in 12s 57 actionable tasks: 7 executed 6PS D:\Project\gradle-demo> 7
执行 gradle clean 命令会删除build目录。
1PS D:\Project\gradle-demo> gradle clean 2 3BUILD SUCCESSFUL in 800ms 41 actionable task: 1 executed 5PS D:\Project\gradle-demo> 6
执行 gradle -v 可以查看版本,用的是本地安装的gradle,配置环境变量的那个gradle。
1PS D:\Project\gradle-demo> gradle -v 2
执行 ./gradlew -v 这里用的是项目中wrapper下的那个gradle。
1PS D:\Project\rcp> ./gradlew -v 2
执行 gradle -h 可以查看帮助。
1PS D:\Project\gradle-demo> gradle -h 2
3. build.gradle
1plugins { 2 id 'java' 3 id 'org.springframework.boot' version '4.0.0' 4 id 'io.spring.dependency-management' version '1.1.7' 5} 6 7group = 'com.example' 8version = '0.0.1-SNAPSHOT' 9description = 'gradle-demo' 10 11java { 12 toolchain { 13 languageVersion = JavaLanguageVersion.of(17) 14 } 15} 16 17configurations { 18 compileOnly { 19 extendsFrom annotationProcessor 20 } 21} 22 23repositories { 24 //配置国内下载源 25 maven{ 26 url 'https://maven.aliyun.com/repository/public/' 27 } 28 mavenCentral() 29} 30 31dependencies { 32 implementation 'org.springframework.boot:spring-boot-starter-webmvc' 33 compileOnly 'org.projectlombok:lombok' 34 developmentOnly 'org.springframework.boot:spring-boot-devtools' 35 annotationProcessor 'org.projectlombok:lombok' 36 testImplementation 'org.springframework.boot:spring-boot-starter-webmvc-test' 37 testRuntimeOnly 'org.junit.platform:junit-platform-launcher' 38} 39 40tasks.named('test') { 41 useJUnitPlatform() 42} 43
- group、name、version:group是module所在组;name是module的名字,同一个组里name具有唯一性;version是module的版本号;group、name、version三者构成了该module的唯一性。
- apply:在module中应用一个插件。和plugins一个意思。
- dependencies:用来声明module所依赖的jar包或其他module。
- repositories:用来声明仓库,告诉程序到哪个仓库去查找对应的module、jar包等依赖。
- task:用来声明module的任务,其对应org.gradle.api.Task。
依赖管理
Gradle依赖的粒度控制相较于Maven也更加精细,maven只有compile、provided、test、runtime四种scope,而gradle有以下几种scope:
- implementation:默认的scope。implementation的作用域会让依赖在编译和运行时均包含在内,但是不会暴露在类库使用者的编译时。举例,如果我们的类库包含了gson,那么其他人使用我们的类库时,编译时不会出现gson的依赖。
- api:和implementation类似,都是编译和运行时都可见的依赖。但是api允许我们将自己类库的依赖暴露给我们类库的使用者。
- compileOnly和runtimeOnly:这两种顾名思义,一种只在编译时可见,一种只在运行时可见,而runtimeOnly和Maven的provided比较接近。
- testImplementation:这种依赖在测试编译时和运行时可见,类似于Maven的test作用域。
- testCompileOnly和testRuntimeOnly:这两种类似于compileOnly和runtimeOnly,但是作用于测试编译时和运行时。
- annotationProcessor:用来声明“只在编译期运行的注解处理器依赖”,不会进入运行时。Java在编译阶段可以运行一类特殊的程序,叫注解处理器,用来:扫描源码中的注解、生成新代码(.java 文件)或在编译期做校验、直接报错。Gradle用annotationProcessor来告诉编译器:这些依赖,是给“编译器”用的,不是给程序运行用的。
通过简短精悍的依赖配置和多种多样的作用与选择,Gradle可以为我们提供比Maven更加优秀的依赖管理功能。
4. Groovy 语法
Groovy是一种基于JVM的动态语言,语法和Java相似,最终也是要编译.class文件在JVM上运行。Groovy完全兼容Java,在此基础上添加了很多动态类型和灵活特性,比如支持闭包,支持DSL(领域特定语言),是一门非常灵活的动态脚本语言。
要执行Groovy的脚本,必须安装Groovy环境,或者使用Java的ClassLoader来调用。
4.1. 变量、方法、类
1、Groovy中使用def关键字来定义变量,可以不指定变量类型,默认访问修饰符是public。
1def a = 1; 2def int b = 1; 3def c = "hello world!"; 4
2、方法使用返回类型或def关键字定义,方法可以接收任意数量的参数,这些参数可以不申明类型,如果不提供可见性修饰符,则该方法为public。如果指定了方法返回类型,可以不需要def关键字来定义方法。如果不使用return,则方法的返回值为最后一行代码的执行结果。
1def add(int a, int b){ 2 println a + b 3} 4 5int minus(a, b){ 6 return a - b 7} 8 9int minus(a, b){ 10 a - b 11} 12
省略:
- 语句后面的分号可以省略。
- 方法的括号可以省略。
- 参数类型可以省略。
return可以省略。
3、Groovy类非常类似Java类。
1def p = new Person() 2 p.increaseAge 5 3 println p.age 4 5class Person{ 6 String name 7 integer age = 10 8 def increaseAge(Integer years){ 9 this.age += years 10 } 11} 12
区别:
- 默认类的修饰符为public。
- 没有可见性修饰符的字段会自动生成对应的setter和getter方法。
- 类不需要与它的源文件有相同的名称,但还是建议采用相同的名称。
4.2. for、switch
1、Groovy支持Java的for(int i=0; i<N; i++)形式的循环语句,另外还支持for in loop形式,支持遍历范围、列表、Map、数组和字符串等多种类型。
1//遍历范围 2def x = 0; 3for(i in 0..3){ 4 x += i 5} 6assert x == 6 7//遍历列表 8def x = 0 9for(i in [0,1,2,3]){ 10 x += i 11} 12assert x == 6 13//遍历Map中的值 14def map = ['a':1, 'b':2, 'c':3] 15x = 0 16for(v in map.values()){ 17 x += v 18} 19assert x == 6 20
2、Groovy中的switch语句不仅兼容Java代码,还可以处理更多的case表达式。case表达式可以是字符串、列表、范围、Integer等等。
1def x = 16 2def result = "" 3 4switch(x){ 5 case "ok": 6 result = "found ok" 7 case [1, 2, 4, 'list']: 8 result = "list" 9 break 10 case 10..19: 11 result = "range" 12 break 13 case Integer: 14 result = "integer" 15 break 16 default: 17 result = "default" 18} 19assert result == "range" 20
4.3. 数据类型
Groovy中的数据类型主要有以下几种:
- Java中的基本数据类型。
- Groovy中的容器类。
- 闭包
4.3.1. 字符串
在Groovy中有两种字符串类型,普通字符串String(java.lang.String)和插值字符串GString(groovy.lang.GString)。
在Groovy中单引号字符串和双引号字符串都可以定义一个字符串常量,只不过单引号字符串不支持插值。
双引号字符串可以支持插值,插值指的是替换字符串中的占位符,占位符表达式是${}或者以$为前缀。
三引号字符串了可以保留文本的换行和缩进符,不支持插值。
1def name = '我是chian人' 2println "hello ${name}" 3println "hello $name" 4 5def str = '''中国人和美国人 6 一起吃火锅 7 好不好?''' 8
String是不可变的,GString是可变的,GString和String即使有相同的字面量,它们的hashCode也可能不同,因此应该避免使用GString作为Map的Key。
1//当双引号字符串中包含插值表达式时,字符串类型为GString。下面断言为true 2assert "one: ${1}".hashCode() != "one: 1".hashCode 3
4.3.2. List
Groovy在Java集合的基础上进行了增强和简化,Groovy的List对应Java中的List接口,默认的实现类为Java中的ArrayList。可以使用as操作符显式指定List的实现类为java.util.LinkedList。使用[]获取List中具有正索引或负索引的元素。
1def number = [1, 2, 3] 2assert number instanceof List 3def linkedList = [1, 2, 3] as LinkedList 4assert linkedList instanceof java.util.LinkedList 5 6def number = [1, 2, 3, 4] 7assert number[1] == 2 8//-1表示列表末尾的第一个元素 9assert number[-1] == 4 10//在列表末尾追加一个元素5 11number << 5 12assert number[4] == 5 13assert number[-1] ==5 14
4.3.3. Map
Groovy中创建Map同样使用[],需要同时指定键和值,默认的实现为java.util.LinkedHashMap。
1def name = [one: 'aaa', tow: "bbb", three: "ccc"] 2assert name['one'] == 'aaa' 3assert name.two == 'bbb' 4
Map中的键关联问题:
1def key = 'name' 2//使用字符串key作为键 3def person = [key: '张三'] 4assert person.containKey('key') 5//使用变量key作为键 6person = [(key): '李四'] 7assert person.contailsKey('name') 8
4.4. 闭包
Groovy中的闭包是一个开放的、匿名的、可以接受参数和返回值的代码块。
定义闭包
1{ [closureParameters -> ] statements } 2
闭包分为两个部分,分别是参数列表部分[closureParameters -> ]和语句部分statements。
参数列表部分是可选的,如果闭包只有一个参数,则参数名是可选的,Groovy会隐式指定it作为参数名。
1{ println it} //使用隐式参数it的闭包 2
当需要指定参数列表时,需要->将参数列表和闭包体分离。
1{ it -> println it} 2 3{ String a, String b -> 4 println "${a} is ${b}" 5} 6
闭包是groovy.lang.Cloush类的一个实例。这使得闭包可以赋值给变量或字段。
1//将闭包赋值给一个变量 2def println = {it -> println it} 3assert println instanceof Closure 4 5//将闭包赋值给Closuer类型变量 6Closuer do = { println 'do!'} 7
调用闭包
1def code = {123} 2assert code() == 123 //闭包当做方法调用 3assert code.call() == 123 //显式调用call方法 4def isOddNumber = { int i -> i%2 != 0} 5assert isOddNumber(3) == true //调用带参数的闭包 6
闭包作为方法参数
1//无参数闭包 2def test1(Closure closure){ 3 println "test1 start..." 4 closuer(); //调用闭包 5 println "test1 end..." 6} 7 8test1{prinln "hahaha"} 9 10//有参数闭包 11def test2(Closuer closure){ 12 int a = 100; 13 int b = 200; 14 println "test2 starte" 15 closure(a,b);//调用闭包 16 println "test2 end" 17} 18 19test2 { x,y ->{ 20 println("x:" + x) 21 println("y:" + y) 22 } 23} 24
5. 构建过程
任何由Gradle构建的项目都遵循这个过程,分为三个阶段:
- Initialization:初始化阶段。按顺序执行init.gradle -> settings.gradle脚本,生成Gradle、Setting、Project对象。
- Configuration:编译阶段,也叫配置阶段。按顺序执行root目录下的build.gradle -> 子项目build.gradle脚本,生成Task执行流程图。
- Execution:执行阶段。按照Task执行图顺序运行每一个Task,完成一个个步骤,生成最终的APK文件或JAR包文件。
整个构建过程,官方在一些节点都设置有hook钩子函数,以便我们在构建过程中添加自己的逻辑。影响构建过程。hook钩子函数可以理解为监听函数,另外也可以设置Linsener监听函数。
5.1. Task
Task是Gradle执行的基本单元。Gradle构建项目是先把所有.gradle脚本都执行一遍,编译生成对应的Gradle、Setting、Project对象。然后根据配置,生成一张Task组成的有向无环图。
最终Gradle会按照图上一个个Task的关联按顺序挨个执行。每个Task都完成一个特定功能,所有Task都执行一遍,则整个项目的构建任务就完成了。
创建Task
1task hello{ 2 println "hello world" 3} 4task (hello2){ 5 println "hello world2" 6} 7task ('hello3'){ 8 println "hello world3" 9} 10task ("hello4"){ 11 println "hello world4" 12} 13
Task的Action
Task内部的Action不是唯一的,而是一个集合,可以往里面添加各种 Action,看下面Task中的声明:
1//在Action队列头部添加action 2Task.doFirst(Action<? supper Task> action); 3Task.doFirst(Closure action); 4//在Action队列头部添加action 5Task.doLast(Action<? supper Task> action); 6Task.doLast(Closure action); 7 8//删除所有的action 9Task deleteAllAction(); 10
doFirst和doLast接受的都是闭包。
- doFirst添加的action最先执行。
- task自身的action在中间执行,无法在task外部添加,需要在自定义task时写。
- doLast添加的action最后执行。
1task speak{ 2 println("This is AA! -- 配置阶段") 3 doFirst{ 4 println("This is doFirst -- inner -- 执行阶段") 5 } 6 doLast{ 7 println("This is doLast -- inner -- 执行阶段") 8 } 9} 10 11speak.doFirst{ 12 println("This is doFirst -- outer -- 执行阶段") 13} 14 15speak.doLast{ 16 println("This is doLast -- outer -- 执行阶段") 17} 18 19
执行任务:
1gradle speak 2
执行结果:
1> Configure project : 2This is AA! -- 配置阶段 3 4> Task :speak 5This is doFirst -- outer -- 执行阶段 6This is doFirst -- inner -- 执行阶段 7This is doLast -- inner -- ִ执行阶段 8This is doLast -- outer -- ִ执行阶段 9
Task依赖
Task依赖是指可以在Task之间指定执行顺序,重点理解dependsOn。
- A.dependsOn B --> 执行A的时候先执行B
- A.mustRunAfter B --> 同时执行A和B,需要先执行B再执行A。若执行关系不成立则报错。
- A.shouldRunAfter B --> 同mustRunAfter,区别:执行关系不成立但是不报错。
1task s1{ 2 doLast{ 3 println("This is s1") 4 } 5} 6task s2{ 7 doLast{ 8 println("This is s2") 9 } 10} 11s1.dependOn s2 12
或者
1task s1{ 2 doLast{ 3 println("This is s1") 4 } 5} 6task s2{ 7 dependOn s1 8 doLast{ 9 println("This is s2") 10 } 11} 12
自定义Task
Gradle中的Task都继承自org.gradle.api.DefaultTask,自定义Task也需要继承这个类,自己写方法,然后加上@TaskAction注解,表示这个方法是Task中的action。可以加多个,按倒序执行。
自定义任务:
1class MyTask extends DefaultTask{ 2 @TaskAction 3 def ss1(){ 4 println("This is MyTask action1") 5 } 6 @TaskAction 7 def ss2(){ 8 println("This is MyTask action2") 9 } 10} 11 12tasks.register('speak', MyTask) { 13 println("This is AA") 14 doFirst { 15 println("This is doFirst") 16 } 17 doLast { 18 println("This is doLast") 19 } 20} 21
执行结果:
1PS D:\Project\gradle-demo> gradle speak 2This is AA 3 4> Task :speak 5This is doFirst 6This is MyTask action2 7This is MyTask action1 8This is doLast 9
系统自带Task
Gradle提供了很多task给我们使用,比如copy、delete等。
设置默认Task
意思是脚本中我们不调用该Task,设置的默认Task也会执行。
1defaultTasks 'aaa', 'bbb' 2 3tasks.register('aaa') { 4 doLast { 5 println("Default aaa task") 6 } 7} 8tasks.register('bbb') { 9 doLast { 10 println("Default bbb task") 11 } 12} 13tasks.register('other') { 14 doLast { 15 println("not a default task") 16 } 17} 18
执行结果
1PS D:\Project\gradle-demo> gradle -q 2Default aaa task 3Default bbb task 4PS D:\Project\gradle-demo> 5
5.2. Plugin
什么是插件
插件只是一组任务,几乎所有的任务,如编译任务,设置域对象,设置源文件等都由插件处理。
Gradle本身只是提供了基本的核心功能,其他特性比如编译Java源码等能力都需要通过插件实现。
应用插件
两种方式:
1plugins { 2 id 'java' 3} 4
或者
1apply plugin: 'java' 2
插件都有自己的短名称,上述两种写法都使用了短名称去应用JavaPlugin。我们还可以用完全的名称:
1apply plugin: org.gradle.api.plugins.JavaPlugin 2
由于Gradle的默认导入,还可以这样写:
1apply plugin: JavaPlugin 2
插件的应用是幂等的,如果一个插件已经被应用,再次应用不会有任何效果。
一个插件是任何实现了Plugin接口的类,Gradle提供了核心插件作为其发行包的一部分,所以核心插件可以像上述方式一样简单应用,但是对于第三方插件,需要进行配置使插件在构建路径中可用。
核心插件:docs.gradle.org/current/use…
第三方插件:plugins.gradle.org/
插件都做了什么
把插件应用到项目中可以用来扩展项目功能。包括:
- 将任务添加到项目(如编译、测试)。
- 使用有用的默认设置对已添加的任务进行预配置。
- 向项目中添加依赖配置。
- 通过扩展对现有类型添加新的属性和方法。
Java插件
Gradle中的Java插件,提供了诸如编译、测试、打包等功能。
Java插件为工程定义了许多默认值。如Java源文件位置。如果遵循这些默认规则,那么无需在脚本文件中书写太多代码。当然,Gradle也允许自定义项目中一些规则。
Java插件向项目中添加了大量任务,如下所示:
对于每个添加到该项目中的源集。Java插件将添加以下编译任务。
Java插件还增加了大量的任务构成该项目的生命周期:
Java项目常用的任务
Java项目(使用了Java插件)执行gradle build后,会默认执行下列任务:
1gradle build 2:compileJava 3:processResources 4:classes 5:jar 6:assemble 7:cpmilerTestJava 8:processTestResources 9:testClasses 10:test 11:check 12:build 13BUILD SUCCESSFUL 14 15Total time: 1.12 secs 16
clean:删除build目录以及所有构建完成的文件。
assemble:编译并打包jar文件,但不会执行单元测试。
test:编译并执行单元测试。
check:编译并测试代码。
《Gradle 基础篇之基础知识的介绍和使用》 是转载文章,点击查看原文。