Gradle学习笔札

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 自学Gradle后的笔记

gradle

1. 基本配置

配置本地化的缓存仓库路径,Gradle是保存再用户目录(C 盘)

1.1. 环境变量

20201218163744

重点是 GRADLE_USER_HOME 这个配置

1.2. 更改镜像源

GRADLE_USER_HOME 目录中建立一个 init.gradle 文件,其内容如下:

allprojects{
   
    repositories {
   
        def ALIYUN_REPOSITORY_URL = 'http://maven.aliyun.com/nexus/content/groups/public'
        def ALIYUN_JCENTER_URL = 'http://maven.aliyun.com/nexus/content/repositories/jcenter'
        all {
    ArtifactRepository repo ->
            if(repo instanceof MavenArtifactRepository){
   
                def url = repo.url.toString()
                if (url.startsWith('https://repo1.maven.org/maven2')) {
   
                    project.logger.lifecycle "Repository ${repo.url} replaced by $ALIYUN_REPOSITORY_URL."
                    remove repo
                }
                if (url.startsWith('https://jcenter.bintray.com/')) {
   
                    project.logger.lifecycle "Repository ${repo.url} replaced by $ALIYUN_JCENTER_URL."
                    remove repo
                }
            }
        }
        maven {
   
            url ALIYUN_REPOSITORY_URL
            url ALIYUN_JCENTER_URL
        }
    }
}

1.3. IDEA配置Gradle

20201218164456

默认情况下,这里会显示我们配置的环境变量信息

1.4. 创建项目

这个与 Maven 比较相似。

1.5. 项目组成

|-- .gradle/                        ------------基包
|-- build/                          ------------基包
|-- src
|   |-- main                        ------------JAVA代码
|   |-- test                        ------------测试
|-- gradle/
|-- build.gradle                    ------------配置文件,与Maven POM相当
|-- gradlew
|-- settings.gradle 

1.5.1. build.gradle'


//配置的是一个Java项目插件
plugins {
    id 'java'
}

// 组织名称
group 'xyz.wongs'
//版本
version '1.0-SNAPSHOT'

//仓库管理
repositories {
    mavenCentral()
}

//依赖库管理
dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.12'
}

1.5.2. 创建程序

执行成功

执行失败

Testing started at 17:12 ...

> Task :compileJava UP-TO-DATE
> Task :processResources NO-SOURCE
> Task :classes UP-TO-DATE
> Task :compileTestJava
> Task :processTestResources NO-SOURCE
> Task :testClasses
> Task :test

expected:<I [Love J]ava> but was:<I []ava>
Expected :I Love Java
Actual   :I ava
<Click to see difference>

junit.framework.ComparisonFailure: expected:<I [Love J]ava> but was:<I []ava>
    at junit.framework.Assert.assertEquals(Assert.java:100)
    at junit.framework.Assert.assertEquals(Assert.java:107)
    at junit.framework.TestCase.assertEquals(TestCase.java:269)
    at xyz.wongs.drunkard.msg.IntfMsgTest.testMsg(IntfMsgTest.java:20)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.runTestClass(JUnitTestClassExecutor.java:110)
    at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:58)
    at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:38)
    at org.gradle.api.internal.tasks.testing.junit.AbstractJUnitTestClassProcessor.processTestClass(AbstractJUnitTestClassProcessor.java:62)
    at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:51)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
    at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
    at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
    at com.sun.proxy.$Proxy2.processTestClass(Unknown Source)
    at org.gradle.api.internal.tasks.testing.worker.TestWorker.processTestClass(TestWorker.java:118)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
    at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:182)
    at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:164)
    at org.gradle.internal.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:412)
    at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
    at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56)
    at java.base/java.lang.Thread.run(Thread.java:834)

xyz.wongs.drunkard.msg.IntfMsgTest > testMsg FAILED
    junit.framework.ComparisonFailure at IntfMsgTest.java:20
1 test completed, 1 failed
> Task :test FAILED
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':test'.
> There were failing tests. See the report at: file:///F:/Repertory/99_WCNGS/gradle/gradle/build/reports/tests/test/index.html
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.
* Get more help at https://help.gradle.org

Deprecated Gradle features were used in this build, making it incompatible with Gradle 7.0.
Use '--warning-mode all' to show the individual deprecation warnings.
See https://docs.gradle.org/6.3/userguide/command_line_interface.html#sec:command_line_warnings
BUILD FAILED in 3s
3 actionable tasks: 2 executed, 1 up-to-date

gradle build test

2. Web项

Gradle创建Web项目,需要有两部,第一个勾选 Java,在选择 Web,否则创建将不成功。

Gradle创建勾选

填写基本信息栏

效果


war{
    archiveName("war3.war")
}

自定义 war 包名称。

3. 控制命令

通过 cmd 来熟悉 Gradle

保证当前路径下有 build.gradle 配置文件,这个与 Maven 相似。

命令行乱码问题:

  • 编码:任务的概念完成,编写 build.gradle ,追加编码任务的配置。

tasks.withType(JavaCompile){
    options.encoding("UTF-8")
}
  • 团队合作,每个人环境设置不一样,造成执行失败

    • 跳过测试:gradle build -x test

测试报告

查看报告详细信息

  • 清空编译结果 clean
  • 硬件资源丰富,开启多线程的并发编译,需要修改配置 GRADLE_USER_HOME ,创建 gradle.properties

//守护方式,新版本默认都守护模式,配置意义不大
org.gradle.daemon=true
// 启用多线程工作环境
org.gradle.parallel=true
  • WEB项目需要考虑不同的打包处理,例如 jarwar
$ gradle jar
$ gradle war
  • 获取执行报告信息

--profile

$ gradle build --profile

获取执行报告信息

4. 整合Junit5

Junit5开始 维护组织已经变更为 org.junit.jupiter

将依赖从一个变成三个依赖库(4.12之后,也是两个不同的依赖库存在)

Junit5 = Platform + Jupiter + Vintage

需要修改配置文件添加依赖

dependencies {
    testCompile group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.7.0'
    testCompile group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.7.0'
    testCompile group: 'org.junit.vintage', name: 'junit-vintage-engine', version: '5.7.0'
    testCompile group: 'org.junit.platform', name: 'junit-platform-launcher', version: '1.7.0'
    
}

gradle 默认还是集成 Junit4,所以需要修改任务配置,追加测试任务的配置

test{
    useJUnitPlatform()
}

Junit5 属于全新架构

5. Wrapper

Maven 相比, Maven 配置过于繁琐,所以 Gradle 操作提供一个 Gradle Wrapper ,利用这个概念,直接在项目中自带 Gradle 的处理环境。

任意目录执行

gradle wrapper
|--- gradlew.bat    ------ Gradle Wrapper简写
|--- .gradle/
|--- gradle/
|---|--- wrapper/
|---|---|--- gradle-wrapper.jar
|---|---|--- gradle-wrapper.properties

利用 gradle wrapper 自定义 Gradle 版本,最明显的变化就是在 gradle-wrapper.propertiesdistributionUrl 的值发生变更。

gradle wrapper --gradle-version XXXX
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

6. 代码分析器

性能、构建环境、依赖库以及程序上的问题, build --scan 代码扫描工具。

gradle build --scan

上传远程服务器分析,这时候需要确认 yes,no

20201221101457

结果返回中:

20201221101542

Publishing build scan...
https://gradle.com/s/iwom6hxwikt5w

我们看到返回要给地址 https://gradle.com/s/iwom6hxwikt5w 这就是报告的完整路径,我们邮箱验证下,即可看到完整的报告。

20201221101821

开发者和服务器的运维人员只需要此报告,就可以无缝沟通。

7. Groovy语法

Groovy = java核心类库 + Python语法形式 + Ruby特点

7.1. Groovy配置

Groovy首页

20201221102826

Gradle 自带 Groovy 处理解析工具,因此不需要单独安装 Groovy

如果想安装学习,深入了解 Groovy ,直接安装 apache-groovy-sdk-x.x.x.zip 压缩包。
解压后,配置下环境变量即可。

C:\Users\WONGS>groovy -v
Groovy Version: 3.0.7 JVM: 1.8.0_151 Vendor: Oracle Corporation OS: Windows 10

7.2. 交互式编程

Groovy 直接提供交互式编程,输入 groovysh 命令:

C:\Users\WONGS>groovysh
Groovy Shell (3.0.7, JVM: 1.8.0_151)
Type ':help' or ':h' for help.
-------------------------------------------------------------------------------
groovy:000>

7.3. 开发工具

  • groovyconsole:不太好用,不推荐
  • idea:需要环境配置,在IDEA直接创建脚本工具。

IDEA配置

在创建中一定要选择 groovy script 选项。

groovy script

后缀为 Grovy.groovy ,依赖JDK版本为 1.8,以后学习尽量以 LTS 为主

7.4. 基础语法

Python 可以 print "内容"


//python 2语法
print "trest" + " 漫威"
//python 3语法
println("trest" + " 漫威")

7.4.1. def关键字

java 1.10 提供一个 var 关键字,根据赋值的类型,自动推断类型

动态更改变量类型,这个与 Python 类型

7.4.2. 数据类型

7.4.2.1. 适配能力强

int msg2 =10
println("【4】"+msg2.getClass()+ "msg2 = "+msg2)
msg2 = 33.2
println("【5】"+msg2.getClass()+ "msg2 = "+msg2)

输出

【4】class java.lang.Integermsg2 = 10
【5】class java.lang.Integermsg2 = 33

7.4.2.2. 数据溢出

def intA = Integer.MAX_VALUE+1
printf("【intA】 = " + intA + " 【intA class】 = " + +intA.getClass())

7.4.2.3. 强制转换

7.4.3. 循环条件

7.4.4. 断言

def num =10

assert num ==100
Caught: Assertion failed: 

assert num ==100
       |   |
       10  false

Assertion failed: 

assert num ==100
       |   |
       10  false

    at xyz.wongs.gry.synx.index.Grovy3.run(Grovy3.groovy:13)

Process finished with exit code 1

7.4.5. 三目运算

String msg ="groovy"

println("原始 [msg] = "+(msg!=null?msg:"WONGS"))
println("原始 [msg] = "+(msg?msg:"WONGS"))
println("原始 [msg] = "+(msg?:"WONGS"))

7.4.6. 范围运算

  • for 循环
int sun = 0

for(i in 0..100)

    sun+=i

println("====>" + sun)
  • 闭包处理函数,将每个当前参数使用 it 描述
('a'..'z').each {
    print(it+",")
}

7.4.7. switch

java 中内容判断不同, Groovy 提供一个范围


def age =18

switch (age){
    case 0..17:
        println('未成年')
        break
    case 18..35:
        println('青年')
        break
    case 36..59:
        println('中年')
        break
    default:
        println('老年')
        break
}

7.4.8. 闭包


//1、闭包编写方式之一
def method={
    return "WONGS.xyz"
}

println("[闭包] "+ method()+ " 类型是 "+ method.getClass())

//2、闭包编写方式之二:没有参数
def method={ ->
    return "WONGS.xyz"
}

println("[闭包] "+ method()+ " 类型是 "+ method.getClass())

//3、闭包编写方式之三:参数
def method={ ->
    return "WONGS.xyz"
}

println("[闭包] "+ method()+ " 类型是 "+ method.getClass())


//
def closMethod1={ String ww ->
    return ww + "WONGS.xyz"
}

println("[closMethod1 闭包] "+ closMethod1("wwww."))

def closMetho2={ int ... args ->
    def sum = 0
    for(int i: args)
        sum += i
    return sum
}

println("[closMetho2 闭包] "+ closMetho2(1,2,3))


// it是 groovy 默认当前参数信息,有参数传递,该it就是描述当前参数;没有的话,则 it 就是 null
def closMetho3={
    return "【当前反馈内容】 WONGS" + it
}

println("[closMetho3 闭包] "+ closMetho3())
println("[closMetho3 闭包] "+ closMetho3("www."))


// 可以省略 return

def closMetho4={
    "【当前反馈内容】 WONGS" + it
}

println("[closMetho3 闭包] "+ closMetho4())
println("[closMetho3 闭包] "+ closMetho4("www."))

闭包的类型

[闭包] WONGS.xyz 类型是 class xyz.wongs.gry.synx.index.Grovy7$_run_closure1

7.4.9. 主方法

只需要一个 static


static void main(String[] args){

    def closMethod1 = {
        it + " wongs.xyz"
    }

    println(closMethod1("wwww"))


}

7.4.10. 数组&列表

程序中的集合主要由数组、列表、Map

  • 数组
// 数组

int[] arrays = new int[]{10,28,45,27,2}
Arrays.sort(arrays)
println(" 直接输出数组"+ arrays)
println(" Arrays 数组转换"+ Arrays.toString(arrays))

// 断言当前数组是否为整型数组
assert arrays instanceof int[]
println(" 获取数组长度 "+ arrays.length)
println(" 首个元素 "+ arrays.first() + " ;最后元素 "+ arrays.last())

数组输出

  • 列表

列表可以看作是 JavaLIST

def list = ["WONGS","@","QQ.COM"]
// 2.1 添加元素
list << "QQ邮箱"
list.add("163邮箱")
println("打印列表 "+ list)

// 2.2 迭代

list.each {
    println("列表内容 -> "+it)
}

list.eachWithIndex{ entry, i ->
    println(i+" 列表内容 -> "+entry)
}
  • Set

參考 List 唯一區別就是需要手工為它設置具體的類型定義,同時也保留了 Set 不允許重複項的設定。


def sets = ["WONGS@QQ.COM","WCNGS@163.COM","IWCNGS@GMAIL.COM","WONGS@QQ.COM","WONGS@HOTMAIL.COM"] as Set
println("Clazz is  "+ sets.class)

sets.eachWithIndex{  entry,  i ->
    println(i+" 列表内容 -> "+entry)
}

保存的順序為 LinkedHashSet ,這一點需要考慮的。


Clazz is  class java.util.LinkedHashSet
0 列表内容 -> WONGS@QQ.COM
1 列表内容 -> WCNGS@163.COM
2 列表内容 -> IWCNGS@GMAIL.COM
3 列表内容 -> WONGS@HOTMAIL.COM
  • Map

def maps = [QQ:"WONGS@QQ.COM",163:"WCNGS@163.COM",GMAIL:"IWCNGS@GMAIL.COM"]
println("Clazz is  "+ sets.class)

// 遍歷
println("遍歷 1  "+ maps['163'])
println("遍歷 2  "+ maps.QQ)

// 添加元素
maps << [189:"189@189.COM"]
// 迭代
maps.eachWithIndex{  entry, i ->
    println("index= "+ i+" ,Key = "+entry.getKey()+" ;Value = "+ entry.getValue())
}

  • 集合嵌套
def listMaps =[
        [QQ:"IWONGS@QQ.COM",GMAIL:"IWCNGS@GMAIL.COM"],
        [QQ:"WONGS@QQ.COM",163:"WCNGS@163.COM"],null
]

println("打印内容  "+ listMaps)
// 获取指定 数据key的内容
println("打印内容  "+ listMaps.QQ)
// 可以匹配最后的 null
println("打印内容  "+ listMaps*.QQ)

7.4.11. 字符串操作

普通字符串 不关心双引号还是单引号,但是再插值字符串必须使用双引号进行定义

  • 字符比较

// 1、字符比较,区分大小写的比较
def str1 = "WONGS@QQ.COM"
def str2 = "WONGS@QQ.COM"
println("【==比较】 "+ (str1==str2))
println("【equals比较】 "+ (str1.equals(str2)))
  • 多行字符
// 2、多行字符串
def str3 = """
    邮箱:
        WONGS@QQ.COM
        主题内容:我喜欢睡觉
    """
println("打印 "+ str3)

// 避免打印过程中第一行出现一个空格 , \ 实现命令结构的拆分操作
def str4 = """\
    邮箱:
        WONGS@QQ.COM
        主题内容:我喜欢睡觉
    """
println("打印 "+ str4)
  • 插值计算
// 3、插值计算
def str5 = "WONGS@QQ.COM"
def str6 = "WONGS@163.COM"
def str7 = "WONGS@GMAIL.COM"
def str8 =  "打印 ${str5}+ ${str6}+ ${str7}"
println(str8)
  • map 填充
// 4、Map填充
def maps = [QQ:"WONGS@QQ.COM",net:"WCNGS@163.COM",GMAIL:"IWCNGS@GMAIL.COM"]
def str9 =  "打印 ${maps.QQ}+ ${maps.net}+ ${maps.GMAIL}"
println("【模式1】"+ str9)

def str10 =  "打印 $maps.QQ+ $maps.net+ $maps.GMAIL"
println("【模式2】"+str10)
  • 转义
// 5、转义字符
def info = [QQ:"WONGS@QQ.COM",net:"WCNGS@163.COM",GMAIL:"IWCNGS@GMAIL.COM"]
def str11 =$/\
    "打印 ${maps.QQ}+ ${maps.net}+ ${maps.GMAIL}
       年薪:19$$
/$
println("【转义字符】"+str11)
  • 正则表达式
// 6、正则表达式

def reg = /\d+/
println("213".matches(reg))

7.4.12. 类

  • 定义

构造方法不需要强制定义,使用时候按照属性要求设置响应内容即可;同时获取属性的时候直接用属性名完成属性内容的获取。

// 1、类定义
class Persion{
    private String name
    private int age
    def getName(){
        return this.name
    }
}
p = new Persion(name:"WONGS")
println("【类定义】"+ p.getName())
  • 对象集合
// 2、对象集合

def persons =[
        new Persion(name:"WONGS"),
        new Persion(name:"Zhang")
]

def handle(msg){
    println("【handle函数处理】"+ msg.@name)
}

// 利用each 闭包特点,将迭代的数据内容传递到 handle(msg)函数中
persons.each {this.&handle}
// 输出全部集合中指定属性内容
println(persons*.getName())

7.4.13. 文件操作

  • 文件写入
baseDir = new File('D:'+File.separator)

new File(baseDir,'mes.txt')
  • 文件读取

new File(baseDir,'mes.txt').eachLine {
    println(it)
}

new File(baseDir,'mes.txt').eachLine(){data,it->
    println("【行号】"+it+ " ;内容 "+ data)
}
  • 目录遍历

baseDir = new File('D:'+File.separator)
baseDir.eachFileMatch(~/.*\.txt/){
    println("【遍历目录】"+it.getName())
}

7.4.14. 多线程

  • 实现
// 1、实现
println("【多线程】 主线程名 "+Thread.currentThread().getName())

Thread.start {
    println("【多线程】 子线程名 "+Thread.currentThread().getName())
    'WONGS'.each {
        print(it+'\t')
    }
}
  • Timer线程
// 2、Timer线程

new Timer().runAfter(10){
    for (int x=0;x<10;x++){
        println("【多线程】 Timer线程 "+Thread.currentThread().getName())
    }
}
  • 线程同步
def ticks = 20
def sale ={->
    synchronized (this){
        TimeUnit.SECONDS.sleep(2)
        println("【多线程】 线程同步 "+Thread.currentThread().getName()+" 】 ,当前剩余票数 "+ ticks--)
    }

}

for (i in 1..<10) {
    def start = Thread.start(sale)
    start.name='Saler - '+i
}
  • JUC操作
CountDownLatch cdl = new CountDownLatch(1)

def first = Thread.start {
    // 第一个线程等待同步
    cdl.await()
    println("【多线程】 First "+Thread.currentThread().getName())
}

def second = Thread.start {
    // 第二个线程 解锁
    TimeUnit.SECONDS.sleep(2)
    println("【多线程】 Second "+Thread.currentThread().getName())
    cdl.countDown()
}
// 先启动第一个线程
first
// 再启动第二个线程
second

我们看下效果输出

【多线程】 Second Thread-2
【多线程】 First Thread-1
  • 线程池
ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors())

for (i in 1..<20) {
    executorService.submit(()->{
        println("【多线程】 线程池 "+Thread.currentThread().getName()+" 序号 "+i)
    })
}

8. Gradle任务

  • 任务列表

gradle -q tasks

看到所有任务列表

14:26:44: Executing task 'tasks --quiet'...

Starting Gradle Daemon...
Gradle Daemon started in 2 s 904 ms

------------------------------------------------------------
Tasks runnable from root project
------------------------------------------------------------

Build tasks
-----------
assemble - Assembles the outputs of this project.
build - Assembles and tests this project.
buildDependents - Assembles and tests this project and all projects that depend on it.
buildNeeded - Assembles and tests this project and all projects it depends on.
classes - Assembles main classes.
clean - Deletes the build directory.
jar - Assembles a jar archive containing the main classes.
testClasses - Assembles test classes.
war - Generates a war archive with all the compiled classes, the web-app content and the libraries.

Build Setup tasks
-----------------
init - Initializes a new Gradle build.
wrapper - Generates Gradle wrapper files.

Documentation tasks
-------------------
javadoc - Generates Javadoc API documentation for the main source code.

Help tasks
----------
buildEnvironment - Displays all buildscript dependencies declared in root project 'gradle'.
components - Displays the components produced by root project 'gradle'. [incubating]
dependencies - Displays all dependencies declared in root project 'gradle'.
dependencyInsight - Displays the insight into a specific dependency in root project 'gradle'.
dependentComponents - Displays the dependent components of components in root project 'gradle'. [incubating]
help - Displays a help message.
model - Displays the configuration model of root project 'gradle'. [incubating]
outgoingVariants - Displays the outgoing variants of root project 'gradle'.
projects - Displays the sub-projects of root project 'gradle'.
properties - Displays the properties of root project 'gradle'.
tasks - Displays the tasks runnable from root project 'gradle' (some of the displayed tasks may belong to subprojects).

Verification tasks
------------------
check - Runs all checks.
test - Runs the unit tests.

Rules
-----
Pattern: clean<TaskName>: Cleans the output files of a task.
Pattern: build<ConfigurationName>: Assembles the artifacts of a configuration.
Pattern: upload<ConfigurationName>: Assembles and uploads the artifacts belonging to a configuration.

To see all tasks and more detail, run gradle tasks --all

To see more detail about a task, run gradle help --task <task>
14:27:10: Task execution finished 'tasks --quiet'.

  • 所有属性

gradle -q properties
14:29:30: Executing task 'properties --quiet'...


------------------------------------------------------------
Root project
------------------------------------------------------------

allprojects: [root project 'gradle', project ':gradle-war3']
ant: org.gradle.api.internal.project.DefaultAntBuilder@7bb06ea9
antBuilderFactory: org.gradle.api.internal.project.DefaultAntBuilderFactory@9dc130d
archivesBaseName: gradle
artifacts: org.gradle.api.internal.artifacts.dsl.DefaultArtifactHandler_Decorated@42339fa
asDynamicObject: DynamicObject for root project 'gradle'
autoTargetJvmDisabled: false
baseClassLoaderScope: org.gradle.api.internal.initialization.DefaultClassLoaderScope@3c61b53b
buildDir: F:\Repertory\99_WCNGS\gradle\gradle\build
buildFile: F:\Repertory\99_WCNGS\gradle\gradle\build.gradle
buildPath: :
buildScriptSource: org.gradle.groovy.scripts.TextResourceScriptSource@44dc4138
buildscript: org.gradle.api.internal.initialization.DefaultScriptHandler@2df166d5
childProjects: {gradle-war3=project ':gradle-war3'}
class: class org.gradle.api.internal.project.DefaultProject_Decorated
classLoaderScope: org.gradle.api.internal.initialization.DefaultClassLoaderScope@30007678
compileJava: task ':compileJava'
compileTestJava: task ':compileTestJava'
components: SoftwareComponentInternal set
configurationActions: org.gradle.configuration.project.DefaultProjectConfigurationActionContainer@179c52e
configurationTargetIdentifier: org.gradle.configuration.ConfigurationTargetIdentifier$1@278a91b8
configurations: configuration container
convention: org.gradle.internal.extensibility.DefaultConvention@16b9c52
defaultArtifacts: extension 'defaultArtifacts'
defaultTasks: []
deferredProjectConfiguration: org.gradle.api.internal.project.DeferredProjectConfiguration@326b99b1
dependencies: org.gradle.api.internal.artifacts.dsl.dependencies.DefaultDependencyHandler_Decorated@7df46a67
dependencyLocking: org.gradle.internal.locking.DefaultDependencyLockingHandler_Decorated@e803330
depth: 0
description: null
displayName: root project 'gradle'
distsDir: F:\Repertory\99_WCNGS\gradle\gradle\build\distributions
distsDirName: distributions
distsDirectory: property(org.gradle.api.file.Directory, map(org.gradle.api.file.Directory property(org.gradle.api.file.Directory, fixed(class org.gradle.api.internal.file.DefaultFilePropertyFactory$FixedDirectory, F:\Repertory\99_WCNGS\gradle\gradle\build)) org.gradle.api.internal.file.DefaultFilePropertyFactory$PathToDirectoryTransformer@5354b7be))
docsDir: F:\Repertory\99_WCNGS\gradle\gradle\build\docs
docsDirName: docs
ext: org.gradle.internal.extensibility.DefaultExtraPropertiesExtension@3d1c0a74
extensions: org.gradle.internal.extensibility.DefaultConvention@16b9c52
fileOperations: org.gradle.api.internal.file.DefaultFileOperations@6365e8e2
fileResolver: org.gradle.api.internal.file.BaseDirFileResolver@6c734e0d
gradle: build 'gradle'
group: xyz.wongs
identityPath: :
inheritedScope: org.gradle.internal.extensibility.ExtensibleDynamicObject$InheritedDynamicObject@1a6ced7a
java: extension 'java'
javaInstalls: org.gradle.jvm.toolchain.internal.DefaultJavaInstallationRegistry@477e1be9
layout: org.gradle.api.internal.file.DefaultProjectLayout@b10d91c
libsDir: F:\Repertory\99_WCNGS\gradle\gradle\build\libs
libsDirName: libs
libsDirectory: property(org.gradle.api.file.Directory, map(org.gradle.api.file.Directory property(org.gradle.api.file.Directory, fixed(class org.gradle.api.internal.file.DefaultFilePropertyFactory$FixedDirectory, F:\Repertory\99_WCNGS\gradle\gradle\build)) org.gradle.api.internal.file.DefaultFilePropertyFactory$PathToDirectoryTransformer@7de5ed55))
listenerBuildOperationDecorator: org.gradle.configuration.internal.DefaultListenerBuildOperationDecorator@1fd8ae
logger: org.gradle.internal.logging.slf4j.OutputEventListenerBackedLogger@37893a17
logging: org.gradle.internal.logging.services.DefaultLoggingManager@3a5ccdc3
model: project :
modelIdentityDisplayName: null
modelRegistry: org.gradle.model.internal.registry.DefaultModelRegistry@68811098
modelSchemaStore: org.gradle.model.internal.manage.schema.extract.DefaultModelSchemaStore@16257f8e
module: org.gradle.api.internal.artifacts.ProjectBackedModule@2bd1d598
mutationState: project :
name: gradle
normalization: org.gradle.normalization.internal.DefaultInputNormalizationHandler_Decorated@6882b6df
objects: org.gradle.api.internal.model.DefaultObjectFactory@34ec5cd9
org.gradle.daemon: true
org.gradle.parallel: true
parent: null
parentIdentifier: null
path: :
pluginManager: org.gradle.api.internal.plugins.DefaultPluginManager_Decorated@54098f0c
plugins: [org.gradle.api.plugins.HelpTasksPlugin@696d430c, org.gradle.buildinit.plugins.BuildInitPlugin@142434b0, org.gradle.buildinit.plugins.WrapperPlugin@4278a879, org.gradle.language.base.plugins.LifecycleBasePlugin@5a913ed5, org.gradle.api.plugins.BasePlugin@59e773a8, org.gradle.api.plugins.ReportingBasePlugin@6c535d90, org.gradle.api.plugins.JavaBasePlugin@6bef1b03, org.gradle.api.plugins.JavaPlugin@38dbe73d]
processOperations: org.gradle.process.internal.DefaultExecActionFactory$DecoratingExecActionFactory@5a43a9bd
project: root project 'gradle'
projectConfigurator: org.gradle.api.internal.project.BuildOperationCrossProjectConfigurator@2c8ad0b8
projectDir: F:\Repertory\99_WCNGS\gradle\gradle
projectEvaluationBroadcaster: ProjectEvaluationListener broadcast
projectEvaluator: org.gradle.configuration.project.LifecycleProjectEvaluator@3faef129
projectPath: :
projectRegistry: org.gradle.api.internal.project.DefaultProjectRegistry@58ab0bee
properties: {...}
providers: org.gradle.api.internal.provider.DefaultProviderFactory_Decorated@1f624f07
publicType: org.gradle.api.plugins.BasePluginConvention
reporting: extension 'reporting'
reportsDir: F:\Repertory\99_WCNGS\gradle\gradle\build\reports
repositories: repository container
resources: org.gradle.api.internal.resources.DefaultResourceHandler@65974343
rootDir: F:\Repertory\99_WCNGS\gradle\gradle
rootProject: root project 'gradle'
script: false
scriptHandlerFactory: org.gradle.api.internal.initialization.DefaultScriptHandlerFactory@2d78d161
scriptPluginFactory: org.gradle.configuration.ScriptPluginFactorySelector@2d8c4b5b
serviceRegistryFactory: org.gradle.internal.service.scopes.ProjectScopeServices$$Lambda$331/356341777@201b339a
services: ProjectScopeServices
sourceCompatibility: 1.8
sourceSets: SourceSet container
standardOutputCapture: org.gradle.internal.logging.services.DefaultLoggingManager@3a5ccdc3
state: project state 'EXECUTED'
status: integration
subprojects: [project ':gradle-war3']
targetCompatibility: 1.8
taskThatOwnsThisObject: null
tasks: task set
test: task ':test'
testReportDir: F:\Repertory\99_WCNGS\gradle\gradle\build\reports\tests
testReportDirName: tests
testResultsDir: F:\Repertory\99_WCNGS\gradle\gradle\build\test-results
testResultsDirName: test-results
version: 1.0-SNAPSHOT
14:29:31: Task execution finished 'properties --quiet'.

8.1. 任务定义

build.gradle 中添加任务,添加完任务,我们在右边的 other 中看到我们定义的任务,如果看不到重新编译下。

task wongs{
    println("当前任务 WONGS")
}

8.1.1. 任务执行

执行

  • 命令行执行

gradle wongs

20201223143726

  • 点击按钮

如图

20201223143352

8.1.2. 任务定义方法

Gradle JavaDoc

版本

接口常量信息

public interface Task extends Comparable<Task>, ExtensionAware {
    String TASK_NAME = "name";
    String TASK_DESCRIPTION = "description";
    String TASK_GROUP = "group";
    String TASK_TYPE = "type";
    String TASK_DEPENDS_ON = "dependsOn";
    String TASK_OVERWRITE = "overwrite";
    String TASK_ACTION = "action";
    String TASK_CONSTRUCTOR_ARGS = "constructorArgs";

Task 接口实例之后,就需要配置

  • 基础语法

def task = task(wongsTask)

task.doLast {
    println("Task I Like Gradle")
}
  • 任务属性设置

def task = task(description:" Xyz.WONGS",wongsTask)

task.doLast {
    println("Task I Like Gradle")
}

结果并没有描述信息


> Task :wongsTask
Task I Like Gradle

gradle help --task wongsTask

这时候我们看输出项的内容非常丰富。

14:52:31: Executing tasks 'help --task wongsTask'...


> Task :help
Detailed task information for wongsTask

Path
     :wongsTask

Type
     Task (org.gradle.api.Task)

Description
      Xyz.WONGS

Group
     -

这些描述信息可以在任务详情帮助进行详细的显示操作。

  • 闭包机制

闭包创建 gradle 任务

task yo{
    description('自定义机制')
    group('xyz.wongs')
    doLast {
        println('WONGS:www.baidu.com')
    }
}
  • 其他常见方式

task(wons){
    description('OUS')
    group('xyz.wongs')
    doLast {
        println('WONGS:www.baidu.com')
    }
}

task(wons,{
    description('OUS')
    group('xyz.wongs')
    doLast {
        println('WONGS:www.baidu.com')
    }
})

以上这两种也是常见的写法。

  • TaskContainer

任务容器,实现任务的创建操作。

20201223151726

tasks.create('wons',{
    description('OUS')
    group('xyz.wongs')
    doLast {
        println('WONGS:www.baidu.com')
    }
})

四种处理语法,按照个人习惯熟悉其中一种即可,在 执行 gradle 命令的时候有两种做法:gradle 任务名称gradle:任务路径,一般建议两者内容要求相同。

8.2. 任务属性

除了基本属性之外,还有一些继承的相关属性存在。

8.2.1. 属性设置

将分组归纳到内置的分组之中,此时可以使用 BasePlugin 常量来进行配置

全局常量

public static final String CLEAN_TASK_NAME = "clean";
public static final String ASSEMBLE_TASK_NAME = "assemble";
public static final String BUILD_GROUP = "build";
public static final String UPLOAD_ARCHIVES_TASK_NAME = "uploadArchives";
public static final String UPLOAD_GROUP = "upload";

def task = task wxy(){
    doFirst {
        println('[BEGIN] WONGS www.baidu.com')
    }
    doLast {
        println('[END] WONGS www.baidu.com')
    }
}

task.group(BasePlugin.BUILD_GROUP)
wxy{
    description "This is My!"
}

8.2.2. 任务继承


def task = task wxy(type:Person){
    description "This is My!"
    // 任务禁用,任务关键性控制
    enabled(true)
    group(BasePlugin.BUILD_GROUP)
    doFirst {
        println('[BEGIN] WONGS www.baidu.com')
    }
    doLast {
        println('[END] WONGS www.baidu.com')
    }
}

class Person extends DefaultTask{
    @TaskAction
    def doSelf(){
        println('[CLAZZ] doSelf')
    }

    @TaskAction
    def doLast(){
        println('[CLAZZ] doLast')
    }
}

8.2.3. 多任务依赖

task xyz(){
    doFirst {
        println('[xyz] WONGS xyz')
    }
    println('[Task] This is xyz')
    doLast {
        println('[xyz] WONGS xyz')
    }
}

task com(dependsOn:[xyz]){
    doFirst {
        println('[com] WONGS com')
    }
    println('[Task] This is com')
    doLast {
        println('[com] WONGS com')
    }
}

task gov(dependsOn:[com]){
    doFirst {
        println('[gov] WONGS gov')
    }
    println('[Task] This is gov')
    doLast {
        println('[gov] WONGS gov')
    }
}

8.2.4. Onlyif

任务创建时候,可以接受一个闭包的处理,根据处理结果判断此任务是否执行。

final String BUILD_NUM = 'wongs'
def Task ot = task xyz(){
    description "This is My!"
    group(BasePlugin.BUILD_GROUP)
    // 类似与 ebable 的形式
    onlyIf {
        // 执行结果
        def execute = false
        if (project.hasProperty('flag')){
            def flag = project.property('flag')
            if (BUILD_NUM==flag){
                execute = true
            }
        }
        return execute
    }
    doLast {
        println('[END] WONGS www.baidu.com')
    }
}
gradle xyz -Pflag=wongs

8.3. 多任务定义

按照定义的顺序来执行,执行顺序并不能起到前后顺序的作用。


task xyz(){
    println('[Task] This is xyz')
}

task com(){
    println('[Task] This is com')
}

task gov(){
    println('[Task] This is gov')
}

如果父任务呈现禁用状态,那么不影响子任务的执行配置。

8.4. 深入任务定义

实际任务当中,我们还会处理存在项目的属性、方法等信息。

在整个 Gradle 项目中的信息 通过 project 得到,对应的属性使用 property() 使用,所有任务实际上都会放在 TaskContainer 进行任务管理, TaskContainer 里面保存的就是一个 Map 集合。

  • 获取任务名称
def task = task (wxy){
    description "This is My!"
    group(BasePlugin.BUILD_GROUP)
    doFirst {
        println('[BEGIN] WONGS www.baidu.com')
        println('[Task Name 1] '+ wxy.name)
        println('[Task Name 2] '+ project.wxy.name)
        println('[Task Name 3] '+ tasks.wxy.name)
        println('[Task Name 4] '+ tasks['wxy'].name)
    }
    doLast {
        println('[END] WONGS www.baidu.com')
    }
}

输入内容如下:

> Task :wxy
[BEGIN] WONGS www.baidu.com
[Task Name] wxy
[Task Name] wxy
[Task Name] wxy
[Task Name] wxy
[END] WONGS www.baidu.com
  • 任务路径

路径名称和任务的名称相同。


def task = task (wxy){
    description "This is My!"
    group(BasePlugin.BUILD_GROUP)
    doFirst {
        println('[BEGIN] WONGS www.baidu.com')
        println('[Task Name 1] '+ wxy.name)
        println('[Task Name 2] '+ project.wxy.name)
        println('[Task Name 3] '+ tasks.wxy.name)
        println('[Task Name 4] '+ tasks['wxy'].name)
        println('[Task Path 1] '+ wxy.path)
        println('[Task Path 2] '+ tasks.getByPath('wxy').path)
        println('[Task Path 3] '+ tasks.getByPath(':wxy').path)
    }
    doLast {
        println('[END] WONGS www.baidu.com')
    }
}

> Task :wxy
[BEGIN] WONGS www.baidu.com
[Task Name 1] wxy
[Task Name 2] wxy
[Task Name 3] wxy
[Task Name 4] wxy
[Task Path 1] :wxy
[Task Path 2] :wxy
[Task Path 3] :wxy
[END] WONGS www.baidu.com
  • 任务属性【重要】

执行多任务的时候如果直接使用 task 形式来创建,这种创建形式下执行将会按照顺序执行,如果此时采用如下语法定义,则可以根据执行的顺序来进行配置


def taskOne = task (taskOne){
    description "This is taskOne!"
    group(BasePlugin.BUILD_GROUP)
    doFirst {
        println('[BEGIN] taskOne')
    }
    doLast {
        println('[END] taskOne')
    }
}

def taskTwo = task (taskTwo){
    description "This is taskTwo!"
    group(BasePlugin.BUILD_GROUP)
    doFirst {
        println('[BEGIN] taskTwo')
    }
    doLast {
        println('[END] taskTwo')
    }
}

这样子我们可以自定义执行顺序


gradle taskTwo taskOne

> Task :taskTwo
[BEGIN] taskTwo
[END] taskTwo

> Task :taskOne
[BEGIN] taskOne
[END] taskOne

很多时候,我们需要某任务1 永远在任务2 之后执行,这种场景通过配置顺序的形式,来定义流程。

实例中我们需要使用一个新名词 mustRunAfter

def taskOne = task (taskOne){
    description "This is taskOne!"
    group(BasePlugin.BUILD_GROUP)
    doFirst {
        println('[BEGIN] taskOne')
    }
    doLast {
        println('[END] taskOne')
    }
}

def taskTwo = task (taskTwo){
    description "This is taskTwo!"
    group(BasePlugin.BUILD_GROUP)
    doFirst {
        println('[BEGIN] taskTwo')
    }
    doLast {
        println('[END] taskTwo')
    }
}

taskOne.mustRunAfter taskTwo

在执行命令时候我们故意让 taskOne 写在 taskTwo 前面。


gradle taskOne taskTwo

结果中我们看到 taskTwotaskOne 之前被执行,这样达到自定义顺序的这样的要求。


> Task :taskTwo
[BEGIN] taskTwo
[END] taskTwo

> Task :taskOne
[BEGIN] taskOne
[END] taskOne
  • 错误依赖

定义任务时候使用 mustRunAfter,如果同时存在依赖关系,这种会进入一种递归任务执行的错误之中。

def taskOne = task (taskOne){
    description "This is taskOne!"
    group(BasePlugin.BUILD_GROUP)
    doFirst {
        println('[BEGIN] taskOne')
    }
    doLast {
        println('[END] taskOne')
    }
}

def taskTwo = task (taskTwo){
    description "This is taskTwo!"
    group(BasePlugin.BUILD_GROUP)
    doFirst {
        println('[BEGIN] taskTwo')
    }
    doLast {
        println('[END] taskTwo')
    }
}

taskTwo.dependsOn(taskOne)
taskOne.mustRunAfter taskTwo
gradle taskOne taskTwo
Circular dependency between the following tasks:
:taskOne
\--- :taskTwo
     \--- :taskOne (*)

* Try:
  • 依赖顺序

任务循环递归处理解决方式,我们换一种比较柔和的模式来完成任务顺序的处理。 这里我们用到 shouldRunAfter


def taskOne = task (taskOne){
    description "This is taskOne!"
    group(BasePlugin.BUILD_GROUP)
    doFirst {
        println('[BEGIN] taskOne')
    }
    doLast {
        println('[END] taskOne')
    }
}

def taskTwo = task (taskTwo){
    description "This is taskTwo!"
    group(BasePlugin.BUILD_GROUP)
    doFirst {
        println('[BEGIN] taskTwo')
    }
    doLast {
        println('[END] taskTwo')
    }
}

taskTwo.dependsOn(taskOne)
taskOne.shouldRunAfter taskTwo
  • 任务替换/重写

按照 JAVA 概念来解释,父类中某个方法不能满足我们要求,那么子类应该考虑自身对该方法进行扩充, Gradle 中有两种重写方式

  • 方式1
task build(overwrite:true){
    doFirst {
        println('[BEGIN] Override build')
    }
}
  • 方式2
def Task task = task([overwrite:true],build){
    doFirst {
        println('[BEGIN] Override build')
    }
}

两种方式的效果是一样的,都是不再使用系统默认 build
任务,而是使用自定义的任务执行操作。

  • 任务失败

def Task t1 = task (taskOne){
    description "This is taskOne!"
    group(BasePlugin.BUILD_GROUP)
    doFirst {
        if (true){
            throw new StopActionException('STOP STOP')
        }
    }
    doLast {
        println('[END] taskOne')
    }
}

def Task t2 = task (taskTwo){
    dependsOn(taskOne)
    description "This is taskTwo!"
    group(BasePlugin.BUILD_GROUP)
    doFirst {
        println('[BEGIN] taskTwo')
    }
    doLast {
        println('[END] taskTwo')
    }
}

gradle taskOne taskTwo
> Task :taskOne
[END] taskOne

> Task :taskTwo
[BEGIN] taskTwo
[END] taskTwo

虽然第一个任务有异常抛出,但是不影响后续其他任务的执行处理。

8.5. 文件处理任务

文件操作在 Gradle 很普遍。

8.5.1. 文件定位

两种获取文件的方式,一种是 Gradle 内置,还有一种就是传统的文件获取方式。


def Task t1 = task (fileTaskOne){
    group(BasePlugin.BUILD_GROUP)
    doFirst {
        File file = file('src/main/resource/application.properties')
        println("[fileTaskOne] file "+ file)
    }
}

def Task t2 = task (fileTaskTwo){
    group(BasePlugin.BUILD_GROUP)
    doFirst {
        File file = file(new File('src'+File.separator+'main'+File.separator+'resource'+File.separator+'application.properties'))
        println("[fileTaskTwo] file "+ file)
    }
}

8.5.2. 资源集合


def Task t1 = task (fileTaskOne){
    group(BasePlugin.BUILD_GROUP)
    doFirst {
        def configurableFileCollection = files('src/main/resource/application.properties','src/main/resource/spring.properties')
        configurableFileCollection.each {
            println(it.name)
        }
    }
}

8.5.3. 集合操作

通过 FileCollection 对文件获取操作,我们需要对集合进行操作。

def Task t1 = task (fileTaskCollection){
    group(BasePlugin.BUILD_GROUP)
    doFirst {
        def configurableFileCollection = files('src/main/resource/application.properties','src/main/resource/spring.properties')
        // 文件集合的相加
        def union = configurableFileCollection+ files('src/main/resource/redis.properties')
        union.each {
            println(it.name)
        }
        println('*' * 40)
        // 文件集合的相减
        def dif = configurableFileCollection-files('src/main/resource/application.properties')
        dif.each {
            println(it.name)
        }
        println('*' * 40)

        println('[相加后的集合] ' +union.size())
        println('[相减后的集合] ' +dif.size())
    }
}

8.5.4. 文件列表

def Task t1 = task (fileTaskList){
    group(BasePlugin.BUILD_GROUP)
    doFirst {
        File file = file('src/main/resources')
        def fileCollection = files(file.listFiles())
        fileCollection.each {
            println(it)
        }
    }
}

8.5.5. 文件树

def Task t1 = task (fileTaskTree){
    group(BasePlugin.BUILD_GROUP)
    doFirst {
        def fileTree = fileTree('src')
        fileTree.exclude('**/*.java','**/java/','**/java/')
        fileTree.include('**/*.properties','**/*.yml')
        fileTree.visit {
            println(it)
        }
    }
}

8.5.6. 源目录文件


sourceSets {
    main{
        java{
            srcDirs('src/main/java')
        }

        resources {
            srcDirs('src/main/resources','src/main/config')
        }
    }
}

def Task t1 = task (fileTaskSource){
    group(BasePlugin.BUILD_GROUP)
    doFirst {
        sourceSets.main.allJava.each {
            println(it)
        }

        println('***' * 50)

        sourceSets.main.resources.each {
            println(it)
        }
    }
}

8.5.7. 文件拷贝

def Task t1 = task (fileTaskCopy,type:Copy){
    from('src/main/resources')
    from('src/main/config')
    from('src/main/java')

    into('build/out')

    include('**/*.xml')
    include('**/*.yml')

    exclude('**/*.java')
}

9. 依赖管理

9.1. 创建可执行Jar文件

配置项

plugins {
    id 'java'
}

group 'xyz.wongs'
version '1.0-SNAPSHOT'

def jdkVersion = 1.8
def CHARSET = 'UTF-8'

// 源代码版本
sourceCompatibility = jdkVersion
targetCompatibility = jdkVersion
// 主程序
def MainClassName = 'xyz.wongs.drunkard.WarApplication'

jar {
    // 生成文件名,不指定,则使用项目名称
    archivesBaseName = 'war3'
    // 设置文件编码
    manifestContentCharset = CHARSET
    // 设置元数据编码
    metadataCharset = CHARSET
    manifest {
        // 程序版本号
        attributes 'Manifest-Version': getArchiveVersion().getOrNull(),
                // 程序主类
                'Main-Class': "$MainClassName",
                //
                'Implementation-Title': 'War3-Gradle',
                //版本编号
                'Implementation-Version': archiveVersion
    }
    // 第三方组件包配置到lib目录
    into('lib') {
        // 运行时的组件
        from configurations.runtime
    }
}

repositories {
    mavenCentral()
}

dependencies {
    testCompile group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.7.0'
    compile group: 'javax.servlet.jsp', name: 'javax.servlet.jsp-api', version: '2.3.3'
    compile group: 'javax.servlet', name: 'javax.servlet-api', version: '4.0.1'
    compile group: 'com.alibaba', name: 'druid', version: '1.2.4'
}
build clean build

build/libs 文件夹中 war3-1.0-SNAPSHOT.jar 打包的文件,这是一个可执行文件

打包后效果

9.2. 依赖库打包范围

打包范围

打包范围 描述
compile 依赖的库文件在编译和运行都需要,前提: plugins {id 'java'} 和 plugins {id 'war'}
providedCompile 在编译需要,前提: plugins {id 'war'}
runtime 运行时需要,编译时不需要,前提: plugins {id 'java'}
testCompile 测试时候需要
debugCompile Debug模式下需要
releaseCompile 依赖文件在release模式下生效
implementation

9.2.1. 本地jar

在默认情况下所有项目中需要使用到的Gradle依赖全部都是通过网络进行下载的, 但是如果说现在某些依赖己经在本地了, 不希望再去通过网络引用 , 那么这一点在Gradle中也是可以的.

dependencies {
    testCompile group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.7.0'
    compile group: 'javax.servlet.jsp', name: 'javax.servlet.jsp-api', version: '2.3.3'
    compile group: 'javax.servlet', name: 'javax.servlet-api', version: '4.0.1'
    compile group: 'com.alibaba', name: 'druid', version: '1.2.4'
    comile fileTree(dir: 'libs',includes: ['*.jar'])
}
  • 第一步. 在项目中添加一个新的目录 libs;
  • 第二步:将本地需要导入的 jar 文件保存在此目录之中;
  • 第三步:修改 build.gradle 配置文件, 在依赖配置上进行本地的引用;

9.2.2. compile替换

在进行Gradle依 赖处理的时候最为常用的肯定是 compile , 但是如果现在要想将其更换为 api , 则需要在项目中引入—些新的插件。


plugins {
    id 'java'
    id 'java-library'
}

group 'xyz.wongs'
version '1.0-SNAPSHOT'

def jdkVersion = 1.8
def CHARSET = 'UTF-8'

// 源代码版本
sourceCompatibility = jdkVersion
targetCompatibility = jdkVersion
// 主程序
def MainClassName = 'xyz.wongs.drunkard.WarApplication'

jar {
    // 生成文件名,不指定,则使用项目名称
    archivesBaseName = 'war3'
    // 设置文件编码
    manifestContentCharset = CHARSET
    // 设置元数据编码
    metadataCharset = CHARSET
    manifest {
        // 程序版本号
        attributes 'Manifest-Version': getArchiveVersion().getOrNull(),
                // 程序主类
                'Main-Class': "$MainClassName",
                //
                'Implementation-Title': 'War3-Gradle',
                //版本编号
                'Implementation-Version': archiveVersion
    }
    // 第三方组件包配置到lib目录
    into('lib') {
        // 运行时的组件
        from configurations.runtime
    }
}

repositories {
    mavenCentral()
}

dependencies {
    testCompile group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.7.0'
    compile group: 'javax.servlet.jsp', name: 'javax.servlet.jsp-api', version: '2.3.3'
    compile group: 'javax.servlet', name: 'javax.servlet-api', version: '4.0.1'
    compile fileTree(dir:'libs', includes: ['*.jar'])
    api 'com.alibaba:druid:1.2.4'
}

使用 api 的处理方式进行打包 的时候, 会发现无法将依赖库生成到最终生成的打包文件之中, 所以如果非必须的时候依然还是建议使用 compile 操作。

9.2.3. 查看依赖信息

gradle -q dependencies
15:21:51: Executing task 'dependencies --quiet'...


------------------------------------------------------------
Project :gradle-war3
------------------------------------------------------------

annotationProcessor - Annotation processors and their dependencies for source set 'main'.
No dependencies

api - API dependencies for source set 'main'. (n)
\--- com.alibaba:druid:1.2.4 (n)

apiElements - API elements for main. (n)
No dependencies

archives - Configuration for archive artifacts. (n)
No dependencies

compileClasspath - Compile classpath for source set 'main'.
+--- com.alibaba:druid:1.2.4
+--- javax.servlet.jsp:javax.servlet.jsp-api:2.3.3
\--- javax.servlet:javax.servlet-api:4.0.1

compileOnly - Compile only dependencies for source set 'main'. (n)
No dependencies

default - Configuration for default artifacts. (n)
No dependencies

implementation - Implementation only dependencies for source set 'main'. (n)
No dependencies

runtimeClasspath - Runtime classpath of source set 'main'.
+--- com.alibaba:druid:1.2.4
+--- javax.servlet.jsp:javax.servlet.jsp-api:2.3.3
\--- javax.servlet:javax.servlet-api:4.0.1

runtimeElements - Elements of runtime for main. (n)
No dependencies

runtimeOnly - Runtime only dependencies for source set 'main'. (n)
No dependencies

testAnnotationProcessor - Annotation processors and their dependencies for source set 'test'.
No dependencies

testCompileClasspath - Compile classpath for source set 'test'.
+--- com.alibaba:druid:1.2.4
+--- javax.servlet.jsp:javax.servlet.jsp-api:2.3.3
+--- javax.servlet:javax.servlet-api:4.0.1
\--- org.junit.jupiter:junit-jupiter-api:5.7.0
     +--- org.junit:junit-bom:5.7.0
     |    +--- org.junit.jupiter:junit-jupiter-api:5.7.0 (c)
     |    \--- org.junit.platform:junit-platform-commons:1.7.0 (c)
     +--- org.apiguardian:apiguardian-api:1.1.0
     +--- org.opentest4j:opentest4j:1.2.0
     \--- org.junit.platform:junit-platform-commons:1.7.0
          +--- org.junit:junit-bom:5.7.0 (*)
          \--- org.apiguardian:apiguardian-api:1.1.0

testCompileOnly - Compile only dependencies for source set 'test'. (n)
No dependencies

testImplementation - Implementation only dependencies for source set 'test'. (n)
No dependencies

testRuntimeClasspath - Runtime classpath of source set 'test'.
+--- com.alibaba:druid:1.2.4
+--- javax.servlet.jsp:javax.servlet.jsp-api:2.3.3
+--- javax.servlet:javax.servlet-api:4.0.1
\--- org.junit.jupiter:junit-jupiter-api:5.7.0
     +--- org.junit:junit-bom:5.7.0
     |    +--- org.junit.jupiter:junit-jupiter-api:5.7.0 (c)
     |    \--- org.junit.platform:junit-platform-commons:1.7.0 (c)
     +--- org.apiguardian:apiguardian-api:1.1.0
     +--- org.opentest4j:opentest4j:1.2.0
     \--- org.junit.platform:junit-platform-commons:1.7.0
          +--- org.junit:junit-bom:5.7.0 (*)
          \--- org.apiguardian:apiguardian-api:1.1.0

testRuntimeOnly - Runtime only dependencies for source set 'test'. (n)
No dependencies

(c) - dependency constraint
(*) - dependencies omitted (listed previously)

(n) - Not resolved (configuration is not meant to be resolved)

A web-based, searchable dependency report is available by adding the --scan option.
15:21:52: Task execution finished 'dependencies --quiet'.

9.2.4. 依赖过滤

有些时候可能并不需要将所有范围的依赖全部进行列出, 只希望列出 Compile 依赖范围所以也可以在依赖列表的时候进行一些参数的配置。

gradle -q dependencies --configuration compile
15:24:51: Executing tasks 'dependencies --configuration compile --quiet'...


------------------------------------------------------------
Project :gradle-war3
------------------------------------------------------------

compile - Dependencies for source set 'main' (deprecated, use 'implementation' instead). (n)
+--- javax.servlet.jsp:javax.servlet.jsp-api:2.3.3 (n)
+--- javax.servlet:javax.servlet-api:4.0.1 (n)
\--- unspecified (n)

(n) - Not resolved (configuration is not meant to be resolved)

A web-based, searchable dependency report is available by adding the --scan option.
15:24:51: Tasks execution finished 'dependencies --configuration compile --quiet'.

9.2.5. 依赖查找

一个项目中有可能会存在有大扭的依赖, 那么也可以直接利用 gradle 项目进行指定依赖名称查找。

范例:查找项目中是否存在有 druid 依赖。

gradle -q dependencyInsight --dependency druid --configuration compile 
15:28:18: Executing tasks 'dependencyInsight --dependency druid --configuration compile --quiet'...

com.alibaba:druid:1.2.4
   variant "runtime" [
      org.gradle.status          = release (not requested)
      org.gradle.usage           = java-runtime (not requested)
      org.gradle.libraryelements = jar (not requested)
      org.gradle.category        = library (not requested)
   ]

com.alibaba:druid:1.2.4
\--- compile

A web-based, searchable dependency report is available by adding the --scan option.
15:28:18: Tasks execution finished 'dependencyInsight --dependency druid --configuration compile --quiet'.

范例:查找项目中是否存在有 spring

gradle -q dependencyInsight --dependency spring --configuration compile 
15:29:28: Executing tasks 'dependencyInsight --dependency spring --configuration compile --quiet'...

No dependencies matching given input were found in configuration ':gradle-war3:compile'
15:29:29: Tasks execution finished 'dependencyInsight --dependency spring --configuration compile --quiet'.

9.3. 依赖库排除

在进行依赖库引入的时候, 由千各个不同的依赖库有可能去引用同— 个依赖文件(日志), 这样就会造成各种的包引入不明确的问题, 所以就需要进行一些依赖的排除操作。

  • 日志依赖

修改 build.gradle 配置文件, 进行一些日志依赖文件的定义。

dependencies {
    testCompile group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.7.0'
    compile group: 'javax.servlet.jsp', name: 'javax.servlet.jsp-api', version: '2.3.3'
    compile group: 'javax.servlet', name: 'javax.servlet-api', version: '4.0.1'
    compile fileTree(dir:'libs', includes: ['*.jar'])
    compile group: 'com.alibaba', name: 'druid', version: '1.2.4'
    compile group: 'org.slf4j', name: 'slf4j-api', version: '1. 7. 30'
    compile group: 'org.slf4j', name: 'slf4j-log4j12', version: '1. 7. 30'
    compile group: 'org.slf4j', name: 'log4j-over-slf4j', version: '1. 7. 30'
    compile group: 'org.slf4j', name: 'slf4j-nop',version: '1. 7. 30'
    compile group: 'org. apache. logging. log4j', name: 'log4j-api',version: '2.13.2'
    compile group: 'org. apache. logging. log4j', name: 'log4j-to-sl f4j',version: '2.13.2'
}
  • 日志文件
<?xml version="1.0" encoding="UTF-8"?>

<configuration>
    <substitutionProperty name="log.base" value="d:/data/log"/>
    <!-- 控制台输出日志 -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%msg%n</pattern>
        </layout>
    </appender>

    <appender name="infoFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>INFO</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <File>${log.base}.log</File>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <FileNamePattern>${log.base}.%d{yyyyMMdd}.log.zip</FileNamePattern>
        </rollingPolicy>
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>%d{HH:mm:ss.SSS} %highlight(%-5level) [%thread] %cyan(%logger) - %msg%n</pattern>
        </layout>
    </appender>

    <appender name="errorFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <!--<Encoding>UTF-8</Encoding>-->
        <File>${log.base}-error.log</File>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <FileNamePattern>${log.base}-error.%d{yyyyMMdd}.log.zip</FileNamePattern>
        </rollingPolicy>
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>%d{HH:mm:ss.SSS} %highlight(%-5level) [%thread] %cyan(%logger) - %msg%n</pattern>
        </layout>
    </appender>

    <root level="INFO">
        <appender-ref ref="CONSOLE" />
        <appender-ref ref="infoFile" />
    </root>

    <root level="ERROR">
        <appender-ref ref="errorFile" />
    </root>

</configuration>
  • 代码编写
package xyz.wongs.drunkard;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author WCNGS@QQ.COM
 * @ClassName ResultCode 定义的接口状态码
 * @Description
 * @Github <a>https://github.com/rothschil</a>
 * @date 2020/12/24 14:36
 * @Version 1.0.0
 */
public class WarApplication {
    private static final Logger LOGGER= LoggerFactory.getLogger(WarApplication.class);

    public static void main(String[] args) {
        LOGGER.info("=========");
        System.out.println("Welcome Gradle World");
        LOGGER.info("=========");
    }
}

执行结果

Caused by: java.lang.IllegalStateException: Detected both log4j-over-slf4j.jar AND bound slf4j-log4j12.jar on the class path, preempting StackOverflowError. See also http://www.slf4j.org/codes.html#log4jDelegationLoop for more details.

这时候出现依赖重复的问题,问题的解决方式有三种:

9.3.1. 依赖排除【推荐】

需要进行依赖的排除处理 修改 build.gradle 配置文件

configurations {
    all*.exclude group: 'org.slf4j', module:'slf4j-log4j12'
}

引入了全局的排除设置,样就表示在进行依赖引入的时候不在项目里面去使用 org.slf4j: slf4j-log4j12

9.3.2. 模块排除

可以针对于个别模块进行排除,


configurations {
    // 1、 全局排除
    //all*.exclude group: 'org.slf4j', module:'slf4j-log4j12'
    // 2、模块排除
    compile.exclude module:'slf4j-log4j12'
}

9.3.3. 依赖排除

可以直接在进行某个依赖配置的时候排除。

compile ('org.slf4j:slf4j-log4j12:1.7.30'){
    exclude module:'log4j-api'
}

9.4. 依赖库版本库号统一管理

项目中新建一个文件 config.gradle ,编写内容如下:

ext{
    jdkVersion = JavaVersion.VERSION_11
    druidVersion = '1.2.4'
}

config.gradle 文件引入 build.gradle


plugins {
    id 'java'
}

apply from : 'config.gradle'

group 'xyz.wongs'
version '1.0-SNAPSHOT'

def CHARSET = 'UTF-8'

// 源代码版本
sourceCompatibility = jdkVersion
targetCompatibility = jdkVersion
// 主程序
def MainClassName = 'xyz.wongs.drunkard.WarApplication'

jar {
    // 生成文件名,不指定,则使用项目名称
    archivesBaseName = 'war3'
    // 设置文件编码
    manifestContentCharset = CHARSET
    // 设置元数据编码
    metadataCharset = CHARSET
    manifest {
        // 程序版本号
        attributes 'Manifest-Version': getArchiveVersion().getOrNull(),
                // 程序主类
                'Main-Class': "$MainClassName",
                //
                'Implementation-Title': 'War3-Gradle',
                //版本编号
                'Implementation-Version': archiveVersion
    }
    // 第三方组件包配置到lib目录
    into('lib') {
        // 运行时的组件
        from configurations.runtime
    }
}

repositories {
    mavenCentral()
}

dependencies {
    testCompile group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.7.0'
    compile group: 'javax.servlet.jsp', name: 'javax.servlet.jsp-api', version: '2.3.3'
    compile group: 'javax.servlet', name: 'javax.servlet-api', version: '4.0.1'
    compile fileTree(dir:'libs', includes: ['*.jar'])
    compile group: 'com.alibaba', name: 'druid', version: druidVersion
    compile group: 'org.slf4j', name: 'slf4j-api', version: '1.7.30'


    compile group: 'org.slf4j', name: 'log4j-over-slf4j', version: '1.7.30'
    compile group: 'org.slf4j', name: 'slf4j-nop',version: '1.7.30'
    compile group: 'org.apache.logging.log4j', name: 'log4j-api',version: '2.13.2'
    compile group: 'org.apache.logging.log4j', name: 'log4j-to-slf4j',version: '2.13.2'
    compile ('org.slf4j:slf4j-log4j12:1.7.30'){
        exclude module:'log4j-api'
    }
}

configurations {
    // 1、 全局排除
    //all*.exclude group: 'org.slf4j', module:'slf4j-log4j12'
    // 2、模块排除
    //compile.exclude module:'slf4j-log4j12'
}

其中 apply from : 'config.gradle' 以及 compile group: 'com.alibaba', name: 'druid', version: druidVersion为变动的。

JDK版本 major.minor.version
1.1 45
1.2 46
1.3 47
1.4 48
1.5 49
1.6 50
1.7 51
1.8 52

命令行进入程序 class 文件路径 执行:

javap -verbose WarApplication.class

9.5. 依赖库集中管理

虽然可以通过单独配置文件模式将所有需要的版本号进行统— 的配置, 但是从某些严格意义上来讲, 如果将所有的依赖都配
置在 build.gradle 文件里面, 那么这个文件的长度就实在太可怕了(回想一下, 传统 Maven 项目之中的配置文件的长度)。

Gradle 中考虑到了传统 Maven 工具所带来的各种的维护问题, 所以对于依赖库的管理最佳的实现模式是建立有一个单独的依赖配置文件, 通过这个依赖配置文件定义所有要使用的依赖, 随后在 build.gradle 文件里面去通过这个依赖文件引入相应的依赖库 ( build.gradle 中不直接参与任何版本的操作)。

  • 【依赖文件】在项目中定义有一个 dependencies.gradle 配置文件;
  • 【编辑文件】修改 dependencies.gradle 文件 , 将所有的依赖库以及对应的版本进行有效定义;
ext.versions = [
    junitVersion:'5.7.0',
    javaxServletJspApi : '2.3.3',
    javaxServletApi:'4.0.1',
    druidVersion:'1.2.4',
    slf4jApi:'1.7.30',
    apacheLog4j:'2.13.2'
]

ext.libraries = [
    'junit-jupiter-api':"org.junit.jupiter:junit-jupiter-api:${versions.junitVersion}",
    'javax.servlet.jsp-api':"javax.servlet.jsp:javax.servlet.jsp-api:${versions.javaxServletJspApi}",
    'javax.servlet-api':"javax.servlet:javax.servlet-api:${versions.javaxServletApi}",
    'druid' :"com.alibaba:druid:${versions.druidVersion}",
    'slf4j-api':"org.slf4j:slf4j-api:${versions.slf4jApi}",
    'log4j-over-slf4j':"org.slf4j:log4j-over-slf4j:${versions.slf4jApi}",
    'slf4j-nop':"org.slf4j:slf4j-nop:${versions.slf4jApi}",
    'log4j-api':"org.apache.logging.log4j:log4j-api:${versions.apacheLog4j}",
    'log4j-to-slf4j':"org.apache.logging.log4j:log4j-to-slf4j:${versions.apacheLog4j}"
]

修改 build.gradle 文件 , 引入以上的依赖库文件 apply from : 'dependencies.gradle'

plugins {
    id 'java'
}

apply from : 'config.gradle'
apply from : 'dependencies.gradle'

group 'xyz.wongs'
version '1.0-SNAPSHOT'

def CHARSET = 'UTF-8'

// 源代码版本
sourceCompatibility = jdkVersion
targetCompatibility = jdkVersion
// 主程序
def MainClassName = 'xyz.wongs.drunkard.WarApplication'

jar {
    // 生成文件名,不指定,则使用项目名称
    archivesBaseName = 'war3'
    // 设置文件编码
    manifestContentCharset = CHARSET
    // 设置元数据编码
    metadataCharset = CHARSET
    manifest {
        // 程序版本号
        attributes 'Manifest-Version': getArchiveVersion().getOrNull(),
                // 程序主类
                'Main-Class': "$MainClassName",
                //
                'Implementation-Title': 'War3-Gradle',
                //版本编号
                'Implementation-Version': archiveVersion
    }
    // 第三方组件包配置到lib目录
    into('lib') {
        // 运行时的组件
        from configurations.runtime
    }
}

repositories {
    mavenCentral()
}

dependencies {
    testCompile(
            libraries.'junit-jupiter-api'
    )
    compile(
            libraries.'javax.servlet.jsp-api',
            libraries.'javax.servlet-api',
            libraries.'druid',
            libraries.'slf4j-api',
            libraries.'log4j-over-slf4j',
            libraries.'slf4j-nop',
            libraries.'log4j-api',
            libraries.'log4j-to-slf4j'
    )
    compile fileTree(dir:'libs', includes: ['*.jar'])

    compile ('org.slf4j:slf4j-log4j12:1.7.30'){
        exclude module:'log4j-api'
    }
}

configurations {
    // 1、 全局排除
    //all*.exclude group: 'org.slf4j', module:'slf4j-log4j12'
    // 2、模块排除
    //compile.exclude module:'slf4j-log4j12'
}

那么此时的Gradle项目就可以通过一个专屈的依赖文件进行所有的依赖库的管理了, 以后如果要维护版本只需要修改专属的依赖配置文件即可(不需要改动 build.gradle 文件)。

10. build.gradle配置

10.1. 日志信息

  • 日志级别
LEVEL 用途
ERROR 错误信息
QUIET 重要消息
WARN 警告信息
LIFECYCKE 进度消息
INFO 一般信息消息
DEBUG 调试信息

task(infoTask){
    doFirst {
        logger.error('[error msg]')
        logger.quiet('[quiet msg]')
        logger.warn('[warn msg]')
        logger.lifecycle('[lifecycle msg]')
        logger.info('[info msg]')
        logger.debug('[debug msg]')
    }
}
  • 日志控制

对千不同级别的日志需要通过任务执行的时候配置不同的日志等级才可以实现输出, 对于日志等级有如下的几个配置项。

选项 级别
默认选项 lifecycle+
-q or -quiet quiet+
-w or -warn warn+
-i or -info info+
-d or -debug debug+

命令行执行


gradle infoTask -d

10.2. 源代码打包

对千任何—个项目来讲, 除了需要将核心的功能程序(编译后的)进行打包处理之外, 实际上还需要考虑的就是开源项目中的源代码的打包操作, 对千源代码的打包操作, 需要单独配暨有一个 Gradle 任务才可以实现。

  • 打包操纵
task(sourceTask,type:Jar){
    // 1、生成 JAR名称
    baseName 'wongs'
    // 2、打包的版本
    version '1.0.0'
    // 3、文件的后缀类型
    classifier 'sources'
    // 4、源码读取路径
    from sourceSets.main.allSource
    // 
    exclude('**/*.xml','**/*.properties') 
    // 5、目标存储路径
    destinationDir file("$buildDir/source-jar")

    manifest {
        attributes 'packageName': 'yootk-sources',
                'Built-By': 'Wongs-Gradle',
                'Built-date': new Date().format('yyyy-MM-dd HH:mm:ss'), 'Manifest-Version': archiveVersion
        }
}

gradle sourceTask

sourceTask执行后的效果

  • 打包时间

当前的操作虽然可以编写任务实现源代码的打包处理操作, 可是有一个问题, 一般的做法是在程序最终编译的时候才进行的打包处理 , 如果按照以上的方式单独定义一个新的任务, 则意味着就需要在整个的程序之中就要单独配置任务, 能否将这些任务合并在一起呢?如果要想实现这样的方式实际上就是任务的依赖关系。

task sourceTask(type:Jar,dependsOn: classes){
    archiveClassifier = 'sources'
    from sourceSets.main.allSource
}

以上的任务仅仅是对已有的任务做了一个扩展, 但是需要注意的是, 毕竟现在的需求是将整个的源代码的打包操作放在 build 之中所以还需要进行一个额外的配置。

// 打包的操作任务
artifacts {
    archives(sourceTask)
}

在源代码打包的过程之中, 如果不希望单独定义一个新的任务, 就和已有任务整合在一起 , 但是所有的新任务一定要在 artifacts 之中进行配置, 否则这些任务将无法正常执行。

10.3. 打包JavaDoc

—个完善的项目在进行打包的时候—般会认为三个 *jar 文件 主程序 (xx-xxx.jar) 、 源代码 (xx-xxx-sources.jar) 、 文档 (xx-xxx-javadoc.Jar) , 如果要想将程序生成 javadoc 文档, 那么首先一定需要在项目中追加相应的文 档说明, 同时还需要在 build.gradle 文件里面定义有相关的任务处理。

  • html网页
task javadocTask(type :Javadoc){
    // 定义所有JAVA代码
    source= sourceSets.main.allJava
}

// 文档编码
tasks.withType(Javadoc){
    options.encoding("UTF-8")
}

gradle javadocTask

文件

HTML内容

  • jar文件

最终在进行项目发布的时候肯定要生成 javadocjar 文件, 所以此时就需要定义有一个打包任务。

HTML 生威 javadoc 才可以打包

plugins {
    id 'java'
}

group 'xyz.wongs'
version '1.0-SNAPSHOT'

repositories {
    mavenCentral()
}

task sourceTask(type:Jar,dependsOn: classes){
    archiveClassifier = 'sources'
    from sourceSets.main.allSource
}

task javadocTask(type :Javadoc){
    // 定义所有JAVA代码
    source = sourceSets.main.allJava
}

task javadocJarTask(type :Jar,dependsOn: javadocTask){
    archiveClassifier = 'javadoc'
    from javadocTask.destinationDir
}

// 打包的操作任务
artifacts {
    archives(sourceTask)
    archives(javadocJarTask)
}

dependencies {
    testCompile group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.7.0'
    testCompile group: 'org.junit.vintage', name: 'junit-vintage-engine', version: '5.7.0'
    testCompile group: 'org.junit.platform', name: 'junit-platform-launcher', version: '1.7.0'
    testCompile group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.7.0'
}

// 文档编码
tasks.withType(Javadoc){
    options.encoding("UTF-8")
    //
    options.addStringOption('Xdoclint:none', '-quiet')
    options.addStringOption('encoding', 'UTF-8')
}

// 程序编译
tasks.withType(JavaCompile){
    options.encoding("UTF-8")
}

test{
    useJUnitPlatform()
}

task(infoTask){
    doFirst {
        logger.error('[error msg]')
        logger.quiet('[quiet msg]')
        logger.warn('[warn msg]')
        logger.lifecycle('[lifecycle msg]')
        logger.info('[info msg]')
        logger.debug('[debug msg]')
    }
}

执行命令

gradle clean build -x test

20201225094444

10.4. 程序测试控制

Gradle 项目中所有的代码肯定都要有相应的测试用例存在, 但是如果说在— 些环境下测试用例可能无法正常执行, 那么面对千这样的情况, 就需要在每次编译打包的时候都使用 -x test 来跨过测试, 但是在 Maven 里面有一种插件, 通过配置可以避免执行测试的代码, 但是在 Gradle 中可没有这样的配置插件, 所以如果要想实现代码测试的跨过, 就需要手工编写程序。

package xyz.wongs.drunkard.msg;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import xyz.wongs.drunkard.msg.impl.IntfMessageImpl;
public class IntfMsgTest {

    @Test
    public void testMsg() {
        IntfMessage intfMessage = new IntfMessageImpl();
        Assertions.assertEquals(intfMessage.getMsg(null), "Love Java");
    }
}

以上代码在执行过程中,会执行不通过,因为 intfMessage.getMsg(null) 返回内容与 Love Java 不相同,结果如下:

expected: <I Love Java> but was: <Love Java>
Expected :I Love Java
Actual   :Love Java

类似这样的测试不成功,我们之前的做法都是通过 -x test 来忽略,但是这样做有些麻烦,这里推荐大家使用编程方式来解决,思路就是做个开关。

build.gradle 添加任务。

// 所有操作准备好触发该任务
gradle.taskGraph.whenReady {
    tasks.each {
        if (it.getName().contains('test')){
            it.enabled(false)
        }
    }
}

当你的项目中配备了这样的处理之后, 实际上连最终的所有的程序的测试都完全失效了, 因为只要发现是测试的指令, 全部都关闭任务处理了。

10.5. 多环境配置管理

项目如果要想正常使用,则一般会经历· 开发环境(dev)、测试环境(beta/test)、生产环境(dev),不同的环境对千—些程序的配置信息也一定会有不同, 那么这样的操作就都可以通过 profile 来进行配置, 按照正常的开发, 应该将所有的网络服务器的信息,保存在不同的 profile 文件里面, 例如, 现在假设在 src/main 目录下创建有一个 profile 公共目录。

我这里为了演示创建一个 profiles 目录,别忘了需要将他设置为 Resource Root

父级目录

// 测试环境
profiles\beta\config\db.properties
// 开发环境
profiles\dev\config\db.properties
// 生产环境
profiles\prod\config\db.properties

以上文件的内容以 KEY-VALUE 形式存在,其中 Key 必须一样。

然后我们设置 build.gradle 内容:我们需要对它 添加环境变量的定义;对源代码选项进行动态设置。


def env = System.getProperty('env')

sourceSets {
    main{
        java{
            srcDirs('src/main/java')
        }
        resources {
            srcDirs('src/main/resources',"src/main/profiles/${env}")
        }
    }
}

执行 gradle build -Denv=beta 操作,其中 -Denv=beta 是我们指定的环境信息。

开始检查 config\db.properties 内容,是否为我们指定环境信息。

打包后内容

长期依赖如果使用 Maven 都会发现有一个核心的问题. 依赖库的版本永远都是需要固定配置, 即便有不同的 profile , 那么依赖库也是无法变更的, 但是在 Gradle 里面 , 将这—问题解决了 , 在之前为了进行所有依赖的统一管理, 提供有 一个 dependencies.gradle 配置文件, 那么如果要想实现不同版本的切换, 就可以通过这样的文件形式来进行控制, 下面构建三个不同的 dependencies-xxx.gradle 文件, 其中代表着不同的配置环境,再通过环境变量信息动态引入。

文件列表如下:


|---gradle/
|---|---build.gradle
|---|---config.gradle
|---|---dependencies-beta.gradle    ---1.1.17
|---|---dependencies-dev.gradle     ---1.1.11
|---|---dependencies-prod.gradle    ---1.2.4

为了演示效果,我们针对三个文件中 druidVersion:'XX' 中的版本号设置不一样。

ext.versions = [
    junitVersion:'5.7.0',
    javaxServletJspApi : '2.3.3',
    javaxServletApi:'4.0.1',
    druidVersion:'xxx',
    slf4jApi:'1.7.30',
    apacheLog4j:'2.13.2'
]

ext.libraries = [
    'junit-jupiter-api':"org.junit.jupiter:junit-jupiter-api:${versions.junitVersion}",
    'javax.servlet.jsp-api':"javax.servlet.jsp:javax.servlet.jsp-api:${versions.javaxServletJspApi}",
    'javax.servlet-api':"javax.servlet:javax.servlet-api:${versions.javaxServletApi}",
    'druid' :"com.alibaba:druid:${versions.druidVersion}",
    'slf4j-api':"org.slf4j:slf4j-api:${versions.slf4jApi}",
    'log4j-over-slf4j':"org.slf4j:log4j-over-slf4j:${versions.slf4jApi}",
    'slf4j-nop':"org.slf4j:slf4j-nop:${versions.slf4jApi}",
    'log4j-api':"org.apache.logging.log4j:log4j-api:${versions.apacheLog4j}",
    'log4j-to-slf4j':"org.apache.logging.log4j:log4j-to-slf4j:${versions.apacheLog4j}"
]

build.gradle 文件中动态引入, apply from : "dependencies-${env}.gradle",这时候我们执行打包操作 gradle build -Denv=dev

动态版本效果

基于这种配置文件编程的构建工具,在整体的使用上就会非常的灵活,各种可能你所需要功能都可以利用编程来完成。

11. 父项目配置

实际上所谓的多项目指的就是 父子项目, 在实际项目开发中, 任何— 个庞大的项目为了方便管理肯定会拆分为若干个不同的子模块, 而后每一个子模块可以单独实现某些功能, 例如:有一个公共的组件模块、 业务模块、 WEB前端模块, 这些子模块需要进行一些特定依赖的使用, 如果现在分别创建这些 Gradle 项目, 则会造成如下的问题

  • Gradle 中一定要进行大丞的结构配置
  • 需要引入大量的第三方依赖库
  • 还需要考虑到一些任务的配置(Junits 编码问题)

如果现在要想进行项目开发, 则一定要将所有的依赖以及公共的任务放在公共的父项目之中, 随后子模块可以继承这些配置。

12. 备注

目录
相关文章
|
5月前
|
Java
graphql dgs springboot gradle学习
https://dgraphql dgs springboot gradle学习
|
Java 测试技术 Scala
[√]groovy / gradle学习
[√]groovy / gradle学习
66 0
|
XML Java Maven
Gradle学习集合整合
Gradle学习集合整合
66 0
|
Java API
Gradle学习基础(3):build脚本基础知识
Gradle学习基础(3):build脚本基础知识
Gradle学习基础(3):build脚本基础知识
Gradle学习基础(1):搭建Gradle环境
Gradle学习基础(1):搭建Gradle环境
Gradle学习基础(1):搭建Gradle环境
|
Java Android开发
Gradle学习基础(2):构建简单的JAVA项目
Gradle学习基础(2):构建简单的JAVA项目
|
缓存 Java Maven
从停用Maven,拥抱Gradle开始,学习SpringBoot
从停用Maven,拥抱Gradle开始,学习SpringBoot
290 0
从停用Maven,拥抱Gradle开始,学习SpringBoot
|
Java Maven fastjson
gradle_学习_02_gradle多模块构建实例
一、前言     二、多模块构建 1.工程结构    父工程:weixin-service 子模块:weixin-gz                weixin-qy   2.
1207 0
|
Java Perl 网络安全
Gradle学习之部署上传项目
 原先在公司做项目时,写了一个简单的基于gradle部署项目的脚本,今天翻出来记录一下  一、build.gradle buildscript { ext { env = System.
1590 0