Gradle (史上最全): 5W字文

文章很长,而且持续更新,建议收藏起来,慢慢读!疯狂创客圈总目录 博客园版 为您奉上珍贵的学习资源 :

  • 免费赠送 :《尼恩Java面试宝典》持续更新+ 史上最全 + 面试必备 2000页+ 面试必备 + 大厂必备 +涨薪必备
  • 免费赠送 经典图书:《Java高并发核心编程(卷1)加强版》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领
  • 免费赠送 经典图书:《Java高并发核心编程(卷2)加强版》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领
  • 免费赠送 经典图书:《Java高并发核心编程(卷3)加强版》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领
  • 免费赠送 经典图书:《尼恩Java面试宝典 V11》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领
  • 免费赠送 资源宝库: Java 必备 百度网盘资源大合集 价值>10000元 加尼恩领取

Gradle是绕不开的一个构建工具

对于用惯了 maven的人来说, Graddle不好用, 非常不好用, 主要是环境 会遇到各种各样的问题

但是,越来越多的 场景使用到了 Graddle,但是

  • spring 的源码,使用 Gradle 构建

  • elasticsearch 的源码,使用 Gradle 构建

  • caffeine 的源码,使用 Gradle 构建

所以 Gradle 的文章,来了

关于 本文的代码,和技术问题, 请来尼恩 发起的Java 高并发 疯狂创客圈 社群交流 ,

什么是Gradle

Gradle是一个基于Apache Ant和Apache Maven概念的项目自动化构建开源工具。它使用一种基于Groovy的特定领域语言(DSL)来声明项目设置,也增加了基于Kotlin语言的kotlin-based DSL,抛弃了基于XML的各种繁琐配置。

面向Java应用为主。当前其支持的语言C++、Java、Groovy、Kotlin、Scala和Swift,计划未来将支持更多的语言。

Projects 和 tasks

projects 和 tasks是 Gradle 中最重要的两个概念。

任何一个 Gradle 构建都是由一个或多个 projects 组成。

每个 project 包括许多可构建组成部分。 这完全取决于你要构建些什么。

举个例子,每个 project 或许是一个 jar 包或者一个 web 应用,它也可以是一个由许多其他项目中产生的 jar 构成的 zip 压缩包。一个 project 不必描述它只能进行构建操作。它也可以部署你的应用或搭建你的环境。

不要担心它像听上去的那样庞大。 Gradle 的 build-by-convention 可以让您来具体定义一个 project 到底该做什么。

每个 project 都由多个 tasks 组成。

每个 task 都代表了构建执行过程中的一个原子性操作。如编译,打包,生成 javadoc,发布到某个仓库等操作。

Hello world

你可以通过在命令行运行 gradle 命令来执行构建,gradle 命令会从当前目录下寻找 build.gradle 文件来执行构建。我们称 build.gradle 文件为构建脚本。

严格来说这其实是一个构建配置脚本,后面你会了解到这个构建脚本定义了一个 project 和一些默认的 task。

要尝试这一点,请创建以下名为 build.gradle 的构建脚本。

第一个构建脚本

build.gradle

task hello {
    doLast {
        println 'Hello world!'
    }
}

Gradle (史上最全): 5W字文_第1张图片

关于 本文的代码,和技术问题, 请来尼恩 发起的Java 高并发 疯狂创客圈 社群交流 ,

快速定义任务

用一种更简洁的方式来定义上面的 hello 任务。

build.gradle

task hello {
    println 'Hello world!'
}

上面的脚本采用闭包的方式来定义了一个叫做 hello 的任务,

闭包也叫做代码块

在两个任务之间指明依赖关系

build.gradle

task foo  {
    println 'crazy maker circle!'
}


task bar(dependsOn: foo)  {
    println " a good circle"
}

Gradle (史上最全): 5W字文_第2张图片

关于 本文的代码,和技术问题, 请来尼恩 发起的Java 高并发 疯狂创客圈 社群交流 ,

为任务增加自定义属性

build.gradle

task myTask {
    ext.myProperty = "myValue"
}

task printTaskProperties << {
    println myTask.myProperty
}

Gradle (史上最全): 5W字文_第3张图片

关于 本文的代码,和技术问题, 请来尼恩 发起的Java 高并发 疯狂创客圈 社群交流

Gradle与maven的对比

Gradle作为基于Groovy语言的构建工具,与Gradle相比,ant显得冗长复杂,maven显得有些局促落后,但Gradle基于DSL语法,特点明显。 简洁、灵活、可读性强

1 .基本构成信息

maven使用. pom文件,

gradle使用. gradle文件基于XML,gradle使用Groovy,gradle使用maven需要固定配置的groupId、artfactId、软件包等基本参数

1 .依赖管理

Gradle的jar软件包管理支持maven的存储库方法和Ant的yedts方法。 因为maven的存储库非常成熟,所以Gradle与存储库兼容,所以非常方便。 定义maven的存储库: maven local ():maven本地库、mavenCentral ) ) : maven远程库、mavenRepo urls:自定义库路径

maven使用组id、名称和版本来查找与配置的资料档案库中指定的依

赖关系相关的Jar包。 虽然Gradle的生命周期更为复杂,但它也支持依赖于Maven的构建生命周期。 compile、runtime、testCompile和testRuntime分别支持项目各个阶段的依赖关系。 它还简化了maven的配置命令

2 .多项目管理

在实际使用中,大多数情况下,需要管理的不仅仅是一个项目。 maven使用依赖、继承和配置的概念,为父模块指定自己的子模块,并提供插件、配置信息等各子模块共有的资源。 通过将封装模块与实现具体功能的模块分开的方法,管理多个项目。

Gradle在这方面更明确,划分更明确,也更容易学习Gradle的多个项目管理。

在eclipse中安装Gradle插件后,创建新的Gradle项目,选择flat-Java-multi项目时,生成的项目就是多个项目管理的一个例子。

Gradle通过settings.Gradle文件指定项目的子项目,在构建初始化阶段(Initialization ),Gradle基于settings.Gradle文件指定哪个子项目初始化每个子项目的项目对象。

由于有很多项目配置,因此我们将简要介绍常见的配置共享和配置独占。

3.多模块项目的Gradle目录结构

示例:我的示例项目demo,我需要有一个common模块用于公用代码,一个rest模块用于提供rest接口,rest依赖common,如果用gradle构建,目录树会是这样:

demo
├── build.gradle   -- 全局配置
├── settings.gradle  -- 全局配置
├── common      -- 子模块1目录
│  └── build.gradle -- 子模块1配置
├── rest       -- 子模块2配置
│  └── build.gradle -- 子模块2配置
...

“所有项目”属性。 返回这个项目对象及其所有子项目。

subProjects属性。 返回这个项目的所有子项目。

项目’ : su B-project-name ’ )来设置相应子项目的配置。

configure(subprojects.findall ) it.name.contains(‘XXX’} )会来寻找符合您需求的子专案。

将您自己的配置添加到子项目的build.gradle文件中。

关于 本文的代码,和技术问题, 请来尼恩 发起的Java 高并发 疯狂创客圈 社群交流

Gradle项目管理工具

基于Groovy的特定领域语言来声明项目设置

Gradle安装(Windows)

1.下载解压版

  • 版本列表:https://services.gradle.org/distributions/
  • v6.5:https://downloads.gradle-dn.com/distributions/gradle-6.5-bin.zip**(用迅雷下载可能会快些)**

2.配置环境变量

  1. gradle_home
    • 变量名:GRADLE_HOME
    • 变量值:D:\tool\gradle-7.5.1-all\gradle-7.5.1(gradle安装目录)
  2. path(编辑)
    • 变量名:Path(编辑)
    • 变量值:%GRADLE_HOME%\bin(增加)
  3. gradle_user_home(在远程仓库下载的jar包保存到该路径下)
    • 变量名:GRADLE_USER_HOME
    • 变量值:D:\dev\maven\repository
D:\dev\jdk\java-1.8.0-openjdk-1.8.0.312-2.b07.dev.redhat.windows.x86_64

D:\tool\Java\jdk-11.0.6

3.配置init.gradle

gradle安装目录\init.d目录下,创建init.gradle文件,

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


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

初始化基本配置:配置远程仓库地址(gradle没有自己的仓库,使用的是maven的仓库)

4.验证是否配置成功

cmd输入命令:

gradle -v

Gradle (史上最全): 5W字文_第4张图片

关于 本文的代码,和技术问题, 请来尼恩 发起的Java 高并发 疯狂创客圈 社群交流

Gradle本地仓库

可能有很多人在纠结这个 Gradle 能不能像 maven 一样手动修改本地仓库,答案当然是肯定的,

而且很简单,只需要在环境变量里面做个配置即可如下图:

Gradle (史上最全): 5W字文_第5张图片

即添加一个名称为 GRADLE_USER_HOME ,值是你想放gradle仓库的地方即可。

GRADLE_HOME=D:\tool\gradle-7.5.1-all\gradle-7.5.1

GRADLE_USER_HOME= D:\dev\maven\repository

GRADLE_USER_HOME= D:\dev\maven\graddleRep

spring-boot-dependencies.version = 2.1.12.RELEASE

 //先从本地仓库寻找,本地仓库没有再从中央仓库寻找
    mavenLocal();
    

Gradle (史上最全): 5W字文_第6张图片

关于 本文的代码,和技术问题, 请来尼恩 发起的Java 高并发 疯狂创客圈 社群交流 ,

Gradle Java 构建入门

Java 插件

如你所见,Gradle 是一个通用工具。它可以通过脚本构建任何你想要实现的东西,真正实现开箱即用。

但前提是你需要在脚本中编写好代码才行。

大部分 Java 项目基本流程都是相似的:编译源文件,进行单元测试,创建 jar 包。

使用 Gradle 做这些工作不必为每个工程都编写代码。

Gradle 已经提供了完美的插件来解决这些问题。

插件就是 Gradle 的扩展,简而言之就是为你添加一些非常有用的默认配置。

Gradle 自带了很多插件,并且你也可以很容易的编写和分享自己的插件。

Java plugin 作为其中之一,为你提供了诸如编译,测试,打包等一些功能。

Java 插件为工程定义了许多默认值,如Java源文件位置。

如果你遵循这些默认规则,那么你无需在你的脚本文件中书写太多代码。

当然,Gradle 也允许你自定义项目中的一些规则,实际上,由于对 Java 工程的构建是基于插件的,那么你也可以完全不用插件自己编写代码来进行构建。

后面的章节我们通过许多深入的例子介绍了如何使用 Java 插件来进行以来管理和多项目构建等。但在这个章节我们需要先了解 Java 插件的基本用法。

一个基本 Java 项目

来看一下下面这个小例子,想用 Java 插件,只需增加如下代码到你的脚本里。

采用 Java 插件

build.gradle

plugins {
    id 'org.springframework.boot' version '2.4.3'
    id 'io.spring.dependency-management' version '1.0.11.RELEASE'
    id 'java'
}

定义一个 Java 项目只需如此而已。

这将会为你添加 Java 插件及其一些内置任务。

添加了哪些任务?

你可以运行 gradle tasks 列出任务列表。这样便可以看到 Java 插件为你添加了哪些任务。

标准目录结构如下:

------------------------------------------------------------
Tasks runnable from root project 'demo'
------------------------------------------------------------

Application tasks
-----------------
bootRun - Runs this project as a Spring Boot application.

Build tasks
-----------
assemble - Assembles the outputs of this project.
bootBuildImage - Builds an OCI image of the application using the output of the bootJar task
bootJar - Assembles an executable jar archive containing the main classes and their dependencies.
bootJarMainClassName - Resolves the name of the application's main class for the bootJar task.
bootRunMainClassName - Resolves the name of the application's main class for the bootRun task.
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.

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 'demo'.
dependencies - Displays all dependencies declared in root project 'demo'.
dependencyInsight - Displays the insight into a specific dependency in root project 'demo'.
dependencyManagement - Displays the dependency management declared in root project 'demo'.
help - Displays a help message.
javaToolchains - Displays the detected java toolchains.
outgoingVariants - Displays the outgoing variants of root project 'demo'.
projects - Displays the sub-projects of root project 'demo'.
properties - Displays the properties of root project 'demo'.
resolvableConfigurations - Displays the configurations that can be resolved in root project 'demo'.
tasks - Displays the tasks runnable from root project 'demo' (some of the displayed tasks may belong to subprojects).


To see more detail about a task, run gradle help --task 


Gradle 默认会从 src/main/java 搜寻打包源码,在 src/test/java 下搜寻测试源码。

并且 src/main/resources 下的所有文件按都会被打包,所有 src/test/resources 下的文件 都会被添加到类路径用以执行测试。

所有文件都输出到 build 下,打包的文件输出到 build/libs 下。

Java 插件的众多任务

Java 插件为你添加了众多任务。

但是它们只是在你需要构建项目的时候才能发挥作用。

最常用的就是 build 任务,这会构建整个项目。

当你执行 gradle build 时,Gralde 会编译并执行单元测试,并且将 src/main/* 下面 class 和资源文件打包。

构建 Java 项目

运行 gradle build 的输出结果

21:03:18: Executing 'build'...


> Configure project :
crazy maker circle!
 a good circle
myValue

> Task :service:compileJava
> Task :service:processResources NO-SOURCE
> Task :service:classes
> Task :service:jar
> Task :app:compileJava
> Task :app:processResources NO-SOURCE
> Task :app:classes
> Task :app:bootJarMainClassName
> Task :app:bootJar
> Task :app:jar SKIPPED
> Task :app:assemble
> Task :app:compileTestJava
> Task :app:processTestResources NO-SOURCE
> Task :app:testClasses

> Task :app:test
2022-11-21 21:03:26.985  INFO 44316 --- [extShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'

> Task :app:check
> Task :app:build

BUILD SUCCESSFUL in 8s
7 actionable tasks: 7 executed
21:03:27: Execution finished 'build'.

Gradle (史上最全): 5W字文_第7张图片

关于 本文的代码,和技术问题, 请来尼恩 发起的Java 高并发 疯狂创客圈 社群交流

其余一些较常用的任务有如下几个:

clean

删除 build 目录以及所有构建完成的文件。

assemble

编译并打包 jar 文件,但不会执行单元测试。一些其他插件可能会增强这个任务的功能。例如,如果采用了 War 插件,这个任务便会为你的项目打出 War 包。

check

编译并测试代码。一些其他插件也可能会增强这个任务的功能。例如,如果采用了 Code-quality 插件,这个任务会额外执行 Checkstyle。

发布 jar 包

如何发布 jar 包?你需要告诉 Gradle 发布到到哪。

在 Gradle 中 jar 包通常被发布到某个仓库中。在下面的例子中,我们会将 jar 包发布到本地目录。当然你也可以发布到远程仓库或多个远程仓库中。

发布 jar 包

build.gradle

uploadArchives {
    repositories {
       flatDir {
           dirs 'repos'
       }
    }
}

执行 gradle uploadArchives 以发布 jar 包。

类似Maven的dependencyManagement方式实现依赖管理

Spring开发了一个类似Maven的依赖关系管理功能的Gradle插件( )。

引入插件
plugins {
  id "io.spring.dependency-management" version "1.0.8.RELEASE"
}

依赖管理配置

你有两个选项来配置依赖项管理:

  1. 使用插件的DSL来直接配置依赖项
  2. 导入一个或者多个已经存在的Maven bom文件

依赖项的管理默认被, 应用到所有的配置或者一个或者多个指定的配置中。

依赖项管理DSL

使用:分隔符的配置方式:

dependencyManagement {
    dependencies {
        dependency 'org.springframework:spring-core:4.0.3.RELEASE'
    }
}

通过groupnameversion进行指定配置:

dependencyManagement {
    dependencies {
        dependency group:'org.springframework', name:'spring-core', version:'4.0.3.RELEASE'
    }
}

通过上面两种方式中的任意一种进行了管理配置之后,在需要引入spring-core的包时,就可以通过如下方式配置依赖:

dependencies {
    compile 'org.springframework:spring-core'
}

这样一个依赖管理配置就完成了。

依赖集配置

如果对于多个模块具有相同的group和version的依赖配置,你可以使用依赖集进行配置。

这样你就只需要指定统一的group和version,而依赖模块不需要再额外指定。

如下所示:

dependencyManagement {
     dependencies {
          dependencySet(group:'org.slf4j', version: '1.7.7') {
               entry 'slf4j-api'
               entry 'slf4j-simple'
          }
     }
}

排除依赖(exclusion)

可以使用Maven的Exclusios语义进行依赖项的排除。
对单个依赖项进行排除:

dependencyManagement {
    dependencies {
        dependency('org.springframework:spring-core:4.0.3.RELEASE') {
            exclude 'commons-logging:commons-logging'
        }
    }
}

也可以对依赖集的entry中进行排除:

dependencyManagement {
    dependencies {
        dependencySet(group:'org.springframework', version: '4.1.4.RELEASE') {
            entry('spring-core') {
                exclude group: 'commons-logging', name: 'commons-logging'
            }
        }
    }
}

导入Maven Bom

插件允许你导入已经存在的Maven Bom来使用其依赖的关系管理:

dependencyManagement {
     imports {
          mavenBom 'io.spring.platform:platform-bom:1.0.1.RELEASE'
     }
}

dependencies {
     compile 'org.springframework.integration:spring-integration-core'
}

这个配置会将Spring框架的Bom的依赖应用到当前的项目

本地Module library依赖

//module需要在项目根目录下的settings.gradle中通过include引入
implementation project(':librarydict')

2.本地二进制library依赖:jar和aar

本地的jar和aar需要放在module的libs文件夹下,通过这种方式依赖的弊端时不知道jar和aar的版本号,如果要按照这种方式依赖,建议将jar/aar的名字加上版本信息,方便确认版本
依赖jar:
//可以一条依赖引入libs下所有的jar
implementation fileTree(dir:'libs',include:['*.jar'])

//也可以指定依赖某一个或几个jar
implementation files('libs/dict-v120.jar','libs/download-v151.jar') 
引入远程maven仓库
//依赖明确的版本,标明group、name和version
implementation group:'con.android.demo',name:'library-dict',version:'1.2.0'

//通常按照如下方式简写即可
implementation 'com.android.demo:library-dict:1.2.0'

//也可以不知道版本,将version改为"+",当远程仓库有更新的版本后,构建时会拉取最新的版本。
//好处是可以始终依赖最新的library:弊端是有可能library的改动导致编译不过或者功能变更不稳定,因为每次都需要检查是否有最新版本,所以构建效率会低一些
implementation 'com.android.demo:library-dict:+'

修改gradle的镜像仓库

改单个项目

在项目的build.gradle文件中,修改repositories配置,

将mavenCentral()改为 maven{ url ‘http://maven.aliyun.com/nexus/content/groups/public/’}, 如:

repositories {
    maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' }
}
更改所有项目

如果想一次更改所有的仓库地址,可以在 USER_HOME/.gradle/文件夹下

添加 init.gradle 文件来配置,如:

allprojects{
    repositories {
        def REPOSITORY_URL = 'http://maven.aliyun.com/nexus/content/groups/public/'
        all { ArtifactRepository repo ->
            if(repo instanceof MavenArtifactRepository){
                def url = repo.url.toString()
                if (url.startsWith('https://repo1.maven.org/maven2') || url.startsWith('https://jcenter.bintray.com/')) {
                    remove repo
                }
            }
        }
        maven {
            url REPOSITORY_URL
        }
    }
}

修改Gradle本地仓库的位置 方法

Gradle的配置
可能有很多人在纠结这个 Gradle 能不能像 maven 一样手动修改本地仓库,答案当然是肯定的,而且很简单,只需要在环境变量里面做个配置即可。

即添加一个名称为 GRADLE_USER_HOME ,值是你想放gradle仓库的地方即可。

  1. 新建系统变量:
GRADLE_USER_HOME
D:\gradle\.gradle\

2.如下图:

Gradle (史上最全): 5W字文_第8张图片

补充说明一点 据说 Gradle有个 Daemon配置,开启这个配置能有效的提高编译速度,具体是不是这样我没有做过对比因为我安装了gradle后就配置了。。。

3.具体操作如下:

在咱们的gradle仓库.gradle目录下创建一个gradle.properties 文件 ,再打开该文件在其中添加如下语句保存即可:

org.gradle.daemon=true  

就这样简单。

Gradle工程目录

gradle-demo
|---src
|---|---main
|---|---|---java(放置正式代码目录)
|---|---|---resources(放置正式配置文件目录)
|---|---|---webapp(放置页面元素)
|---|---test
|---|---|---java(放置单元测试代码目录)
|---|---|---resources(放置测试配置文件目录)
|---build.gradle
|---settings.gradle

关于 本文的代码,和技术问题, 请来尼恩 发起的Java 高并发 疯狂创客圈 社群交流 ,

groovy编程语言

Gradle在idea中使用

  1. idea中,使用groovy语言编辑器,

    Tools - Groovy Console

  2. 方法调用,省略分号,小括号

  3. 定义字符串,单引号定义普通字符串,双引号可以引用变量,三引号按格式定义字符串

输出

//介绍groovy编程语言
println("hello groovy"); // idea快捷键sout
println("hello groovy") // 可以省略最后的分号
println "hello groovy" // 可以省略括号
println 12 + ":" + 24

println 12 + 24

Gradle (史上最全): 5W字文_第9张图片

关于 本文的代码,和技术问题, 请来尼恩 发起的Java 高并发 疯狂创客圈 社群交流

定义变量

// groovy 中定义变量
// def 是弱类型,groovy会自动根据情况来给变量赋予对应的类型
def foo = 18;
def bar = 疯狂创客圈 
println foo;


字符串定义

方式1:单引号定义字符串

使用 '...' 单引号来定义字符串,这种定义的字符串跟 Java 中定义的字符串是一样的,都是不可变的。

并且通过 str1.class 可以验证方式1输出的字符串类型就是 java.lang.String

方式2:双引号定义字符串

使用 "..." 双引号来定义字符串,这种定义的字符串跟 Java 中定义的字符串是一样的,都是不可变的。

并且通过 str1.class 可以验证方式2输出的字符串类型就是 java.lang.String


str1 = '疯狂创客圈'  // 定义普通字符串
str2 = "name:${str1}" //可以引用变量
str3 = '''name:xiaoming
age:18'''  //按格式定义字符串
println str1
println str1.class
println str2
println str2.class //class org.codehaus.groovy.runtime.GStringImpl


通过上面的例子可以看出,通过在str2中拼接 ${变量/表达式} 之后输出的 class 类型不再是 java.lang.String 而是 org.codehaus.groovy.runtime.GStringImpl

很明显这个类型就是 GString 的实现类。

因此可以知道单引号和双引号定义的字符串的区别在双引号定义的字符串可以通过 $ 进行扩展,而单引号定义的字符串是不可变的,因此不可以扩展。

方式3:三个单引号定义字符串

使用 '''...''' 双引号来定义字符串,这种定义的字符串跟 Java 中定义的字符串是一样的,都是不可变的。

并且通过 str3.class 可以验证方式3输出的字符串类型就是 java.lang.String

str1 = '疯狂创客圈'  // 定义普通字符串


//这里会有一个换行符号\n
def str5 ='''
疯狂创客圈  一个java 高并发研究社群
Hello  疯狂创客圈
    疯狂创客圈
    
${str1} 是一个java 高并发研究社群
'''
println str5.class //class org.codehaus.groovy.runtime.GStringImpl

assert  str5.startsWith("\n")//验证 str5 是以 \n 开头的

方式3对字符串的样式进行修改,例如换行,缩进。

Gradle (史上最全): 5W字文_第10张图片

关于 本文的代码,和技术问题, 请来尼恩 发起的Java 高并发 疯狂创客圈 社群交流

方式4:三个双引号定义字符串
str1 = '疯狂创客圈'  // 定义普通字符串


//这里会有一个换行符号\n
def str5 ="""
疯狂创客圈  一个java 高并发研究社群
Hello  疯狂创客圈
    疯狂创客圈
    
${str1} 是一个java 高并发研究社群
"""

println str5
println str5.class //class org.codehaus.groovy.runtime.GStringImpl

assert  str5.startsWith("\n")//验证 str5 是以 \n 开头的

Gradle (史上最全): 5W字文_第11张图片

关于 本文的代码,和技术问题, 请来尼恩 发起的Java 高并发 疯狂创客圈 社群交流

定义一个集合类型



def list = ['a','b']
// 往list中添加元素
list.add("疯狂创客圈")
list.add("java高并发社群")
list << 'c'
// 取出list中第3个元素
println list.get(2)


定义一个map


def map = ['key1':'value1','key2':'value2']
// 向map中添加键值对
map.key3 = 'value3'
// 打印出key3的值
println map.get('key3')

闭包(无参数)

// groovy 中的闭包
// 什么是闭包?闭包其实就是一段代码块。在gradle中,我们主要是把闭包当参数来使用
// 闭包省略()
//plugins({
//    id('java')
//})
//相当于
//plugins {
//    id 'java'
//}

// 定义一个闭包
def b1 = {
    println "hello b1"
}
// 定义一个方法,方法里面需要闭包类型的参数
def method1(Closure closure) {
    //closure()
    closure.call()
}
// 调用方法method1
method1(b1)
method1 b1

method1({
    println "xiaoming"
})

method1 {
    println "xiaoming"
}

闭包(有参数)

//定义一个闭包,带参数
def b2 = {
    v ->
        println "hello ${v}"
}
// 定义一个方法,方法里面需要闭包类型的参数
def method2(Closure closure){
    //closure("xiaoma")
    closure.call("xiaoma")
}
// 调用方法method2
method2 b2
method2 {
    v ->
        println "hello ${v}"
}

断言 assert

where = 疯狂创客圈
assert where == 疯狂创客圈

Gradle (史上最全): 5W字文_第12张图片

关于 本文的代码,和技术问题, 请来尼恩 发起的Java 高并发 疯狂创客圈 社群交流

gradle构建脚本介绍

gradle构建脚本介绍(build.gradle)
两个重要的概念 project 和 task
任何一个Gradle构建都由一个或多个project组成,每个project包括许多的构建部分
每个project由一个或多个Task组成,每个Task表示在构建执行过程中的一个原子操作
构建Project完成后会生成一个jar或者war文件,构建过程中Gradle基于build.gradle实例化一个org.gradle.api.Project对象,并通过project变量来隐式的调用其成员变量
Project对象属性:project(隐式使用)、group、name、version、path、description、projectDir、buildDir、ant
其它常用配置:plugins(apply plugin)、dependencies、repositories、task、ext(gradle.properties 属性配置)

Gradle管理jar包

build.gradle文件的基本内容

plugins {
    id 'java'
    // web工程需要的插件
    id 'war'
}

version '1.0-SNAPSHOT'
group 'xin.yangshuai'

sourceCompatibility = 1.8

/*
 * 指定所使用的仓库
 */
repositories {
    // Gradle没有自己的中央仓库
    // 先从本地maven仓库寻找依赖的jar包,存在则直接使用本地maven仓库的jar
    mavenLocal()
    // 本地maven仓库没有则从远程仓库下载jar包,并保存到 环境变量GRADLE_USER_HOME 配置的路径下
    // 设置maven仓库阿里镜像地址
    maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' }
    // 配置使用Maven的中央仓库
    mavenCentral()
}

/**
 * 配置依赖的jar包
 */
dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.12'
    /* web工程需要的jar包 */
    compile group: 'org.springframework', name: 'spring-context', version: '5.0.2.RELEASE'
    compile group: 'org.springframework', name: 'spring-web', version: '5.0.2.RELEASE'
    compile group: 'org.springframework', name: 'spring-webmvc', version: '5.0.2.RELEASE'
    providedCompile group: 'javax.servlet', name: 'javax.servlet-api', version: '3.0.1'
    providedCompile group: 'javax.servlet', name: 'jsp-api', version: '2.0'
}

配置gradle的repositories仓库

mavenLocal()的配置
  1. mavenLocal()表示使用本地maven仓库
  2. mavenCentral()使用maven中心仓库
  3. 使用阿里云(maven {url ‘http://maven.aliyun.com/nexus/content/groups/public/’})#这个相当于使用自定义maven仓库地址的配置,可以是私服。
  4. JCenter是Goovy Grape内的默认仓库,Gradle内建支持jcenter()仓库,项目库打包成aar文件类似jar,只是多了res,lib文件的包,上传到jcenter后,可以很方面的在本地调用库文件
repositories {


    mavenLocal()
//        mavenCentral()

    maven {url 'https://maven.aliyun.com/repository/public/';}


    maven { url 'https://maven.aliyun.com/repository/jcenter' }
    maven { url 'https://maven.aliyun.com/repository/google' }
    maven { url 'https://maven.aliyun.com/repository/central' }
    maven { url 'https://maven.aliyun.com/repository/gradle-plugin' }
}

gradle默认本地依赖存储位置

因为Gradle如果不配置环境变量,则会在用户目录下(在Windows下是C盘的目录)创建.gradle目录,并将下载的依赖文件保存在这个目录里。

如果不想使用缺省目录,则可以设置环境变量GRADLE_USER_HOME的路径,就可改变gradle的缓存目录。

使用maven本地仓库

#配置M2_HOME
#mavenLocal() 时Gradle默认会按以下顺序去查找本地的maven仓库:
#  USER_HOME/.m2/settings.xml  > M2_HOME/conf/settings.xml >  USER_HOME/.m2/repository。


  1. 修改build.gradle文件,配置maven本地仓库mavenLocal()
repositories {
    mavenLocal()
}

  1. 使用maven本地仓库,需要告知gradle本地maven仓库的地址,配置

    M2_HOME
    

    环境变量,

    M2_HOME   /conf/settings.xml
    

    中指定我们本地仓库的位置

    • 变量名:M2_HOME
    • 变量值:D:\tool\apache-maven-3.6.0(maven安装路径)

当我们使用maven本地仓库(mavenLocal())时,jar包会直接利用本地maven仓库,而不会去下载

**参考:**https://blog.csdn.net/yzpbright/article/details/89001633?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.nonecase

注意: gradle寻找本地maven仓库位置的策略

USER_HOME  /.m2/settings.xml` >> `M2_HOME/conf/settings.xml` >> `USER_HOME/.m2/repository

1、我们一般在maven的安装目录/conf/settings.xml(也就是我们配置的maven环境变量)中配置本地仓库位置,所以我们需要让gradle选择该路径,从而使用我们配置的maven本地仓库

2、gradle先寻找USER_HOME/.m2/settings.xml,所以我们要删掉该文件(其实也可以将安装目录下的settings.xml复制过来)

3、maven环境变量我们习惯配置成MAVEN_HOME,但是gradle寻找的是M2_HOME,所以我们需要配置M2_HOME环境变量

验证: 切换使用mavenCentral()和mavenLocal(),查看jar包引用地址

使用远程仓库
  1. 修改build.gradle文件,配置maven中央仓库
repositories {
    mavenCentral()
}

  1. 当需要在远程仓库下载jar包时,会将jar包保存到GRADLE_USER_HOME\caches\modules-2\files-2.1\文件夹下,配置 GRADLE_USER_HOME 环境变量,也可以直接在idea中指定位置
    • 变量名:GRADLE_USER_HOME
    • 变量值:D:\work\gradleCK

其它可用的远程仓库地址

repositories {
    mavenLocal()
    // 阿里
    maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' }
    mavenCentral()
}

关于gradle中jar包引用的说明

如果我们在repositories{}中先配置mavenLocal()(maven本地仓库),再配置mavenCentral()(远程仓库)

那么gradle引用jar的位置就会出现两个,如果本地maven仓库中存在则直接引用本地maven仓库中的jar包

如果本地maven仓库中不存在,则会连接远程仓库地址,将jar包下载到gradle本地缓存地址,并且引用该地址下的jar包。

注意: gradle并不会向本地maven仓库中下载jar包,gradle缓存路径下的jar包与maven本地仓库中的jar包的格式也有区别。

**参考:**https://blog.csdn.net/feinifi/article/details/81458639

验证: 查看项目中不同jar包的引用地址(maven仓库中存在的jar包和不存在的jar包,观看其引用的地址)

gradle中jar包的依赖配置

jar包依赖方式
/**
 * 配置依赖的jar包
 * gradle工程所有的jar包的坐标都在dependencies属性内放置
 * 每一个jar包的坐标都有三个基本元素组成 group,name,version
 * 添加坐标的时候都要带上jar包的作用域
 */
dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.12'
}

jar包的依赖范围(作用域)

jar包的依赖范围(作用域):
compile:编译阶段:主程序、测试程序均有效;运行阶段:均有效
providedCompile:编译阶段:主程序、测试程序均有效;运行阶段:均无效(依靠运行时环境提供的jar包)
runtime:编译阶段:主程序、测试程序均无效(无需依赖);运行阶段:均有效
testCompile:编译阶段:主程序无效,测试程序有效;运行阶段:均有效
testRuntime:编译阶段:主程序、测试程序均无效(无需依赖);运行阶段:测试程序有效

jar包依赖版本冲突默认解决方式

jar包依赖具有传递性
依赖jar包版本存在冲突解决方案:
maven:最短路径原则,先声明原则
gradle:选择最高版本

修改jar包依赖的配置策略
configurations.all {
    resolutionStrategy {
        // 配置后将不自动处理jar包版本冲突,可以与依赖的排除配合使用
        failOnVersionConflict()
        // 强制使用某个版本的jar包,覆盖依照gradle默认策略引用的jar包
        force 'org.slf4j:slf4j-api:1.7.24'
    }
}

直接在build.gradle中配置configurations.all{}

依赖的排除
dependencies {
    compile(group: 'org.hibernate', name: 'hibernate-core', version: '3.6.3.Final') {
        // 排除某个依赖,其中 module 相当于 jar包坐标中的 name
        exclude(group: 'org.slf4j', module: 'slf4j-api')
    }
}

任务(Task)的编写

任务(task)的主要操作动作

  • dependsOn:依赖相关操作,定义任务时参数依赖、任务内部依赖、外部添加依赖
  • doFirst:任务执行之前执行的方法
  • doLast(<<(旧功能,不提倡)):任务执行之后执行的

task t1 {
    doFirst {
        println 't1 do first'
    }
    println 'hello t1'
    doLast {
        println 't1 do last'
    }
}

// 参数依赖
task t2(dependsOn: 't1') {
    // t2执行前操作
    doFirst {
        println 't2 do first'
    }
    println 'hello t2'
    // t2 执行后操作
    doLast {
        println 't2 do last'
    }
}
// 结论:在构建Project时,只执行直接定义在task下(非doFirst和doLast闭包中)的代码,有依赖也不会再次执行依赖的task
// 直接调用task时,只执行在doFirst或doLast闭包中的代码,并且会优先执行依赖的task的doFirst或doLast闭包中的代码
task t3 {
    // 任务内部依赖
    dependsOn 't1'
    println 'hello t3'
    doLast {
        println 't3 do last'
    }
}

task t4 {
    doFirst {
        println 't4 do first'
    }
    println 'hello t4'
}
// 外部添加依赖
t4.dependsOn 't1'

//动态任务
4.times {
    val ->
        task "task${val}" {
            doFirst {
                println "The task is task${val}"
            }
            println "hello task${val}"
        }
}

task t5 {
    // 给任务添加自定义属性
    ext.myProperty = 'The property value'
    doFirst {
        println "t5 ${myProperty}"
    }
    println 'hello t5'
}

Gradle项目构建生命周期

Gradle项目构建生命周期
初始化阶段:

通过settings.gradle判断哪些项目需要初始化,加载所有需要初始化的项目的build.gradle文件并为每个项目创建project对象
配置阶段:

执行各项目下的build.gradle脚本,完成project的配置,并且构造task任务关系图以便在执行阶段按照依赖关系执行task中的配置代码(直接定义在task下的代码,配置阶段就需要执行)

执行阶段:

通过配置阶段的task图,按顺序执行需要执行的任务中的动作代码(任务调用才会执行的代码,定义在doFirst或doLast中的代码)

关于 本文的代码,和技术问题, 请来尼恩 发起的Java 高并发 疯狂创客圈 社群交流 ,

Gradle项目构建生命周期的钩子方法

我们可以直接覆写这些方法,在方法中加入我们自己的逻辑,(写在build.gradle或者settings.gradle中)

// 项目构建之前
gradle.settingsEvaluated {
    println "初始化阶段0 settingsEvaluated"
}
gradle.projectsLoaded {
    println "初始化阶段1 projectsLoaded"
}
// 配置阶段
gradle.beforeProject {
    println "配置阶段0 beforeProject"
}
gradle.afterProject {
    println "配置阶段1 afterProject"
}
gradle.projectsEvaluated {
    println "配置阶段2 projectsEvaluated"
}
gradle.taskGraph.whenReady {
    println "配置阶段3 taskGraph.whenReady"
}
// 执行阶段
gradle.taskGraph.beforeTask {
    println "执行阶段0 taskGraph.beforeTask"
}
gradle.taskGraph.afterTask {
    println "执行阶段1 taskGraph.afterTask"
}
gradle.buildFinished {
    println "执行阶段2 buildFinished"
}

Gradle创建多模块项目

父模块有settings.gradle文件,子模块没有settings.gradle文件,

settings文件

settings中配置所拥有的子模块

Gradle (史上最全): 5W字文_第13张图片

rootProject.name = 'demo'
include 'service'
include 'app'



关于 本文的代码,和技术问题, 请来尼恩 发起的Java 高并发 疯狂创客圈 社群交流

父模块build.gradle

父模块build.gradle中配置公用的信息,插件、依赖等

// 配置统一信息,包括root模块
allprojects {
    // 统一引入 java 插件,并指定版本(注:不能将plugins {id 'java'} 直接挪到 allprojects 里面,变换一下形式)
    apply plugin: 'java'
    sourceCompatibility = 1.8
    // 统一配置公共属性,例如:group、version
    group 'xin.yangshuai'
    version '1.0-SNAPSHOT'
}
// 子模块配置统一信息
subprojects {
    // 配置公用的资源库
    repositories {
        mavenCentral()
    }
	// 配置公用的依赖
    dependencies {
        compile 'ch.qos.logback:logback-classic:1.2.2'
    }
}
dependencies {
}

Gradle (史上最全): 5W字文_第14张图片

关于 本文的代码,和技术问题, 请来尼恩 发起的Java 高并发 疯狂创客圈 社群交流 ,

子模块build.gradle

子模块build.gradle中配置特有的信息

apply plugin: 'war'

//如果使用plugins引用插件,则buildscript必须放到plugins前面
buildscript {
    repositories {
        maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' }
        jcenter()
    }
    dependencies {
        classpath 'com.bmuschko:gradle-tomcat-plugin:2.5'
    }
}

dependencies {
    compile project(':core')

    // providedCompile:只在编译时依赖,运行环境下不需要
    providedCompile 'javax.servlet:javax.servlet-api:3.1.0'
}

Gradle (史上最全): 5W字文_第15张图片

关于 本文的代码,和技术问题, 请来尼恩 发起的Java 高并发 疯狂创客圈 社群交流

常用的Gradle的5种依赖详解

依赖类型

dependencies DSL标签是标准Gradle API中的一部分,而不是Android Gradle插件的特性,所以它不属于android标签。

Gradle依赖有三种方式,如下面的例子:

apply plugin: 'com.android.application'
 
android { ... }
 
dependencies {
    // Dependency on a local library module
    implementation project(":mylibrary")
 
    // Dependency on local binaries
    implementation fileTree(dir: 'libs', include: ['*.jar'])
 
    // Dependency on a remote binary
    implementation 'com.example.android:app-magic:12.3'
}
本地library模块依赖
implementation project(":mylibrary")

这种依赖方式是直接依赖本地库工程代码的,

需要注意的是,mylibrary的名字必须匹配在settings.gradle中include标签下定义的模块名字。

本地二进制依赖
implementation fileTree(dir: 'libs', include: ['*.jar'])

这种依赖方式是依赖工程中的 module_name/libs/目录下的Jar文件

注意Gradle的路径是相对于build.gradle文件来读取的,所以上面是这样的相对路径。

如果只想依赖单个特定本地二进制库,可以如下配置:

implementation files('libs/foo.jar', 'libs/bar.jar')
远程二进制依赖
implementation 'com.example.android:app-magic:12.3'

上面是简写的方式,这种依赖完整的写法如下:

implementation group: 'com.example.android', name: 'app-magic', version: '12.3'

group、name、version共同定位一个远程依赖库。

需要注意的点是,version 最好不要写成"12.3+"这种方式,除非有明确的预期,因为非预期的版本更新会带来构建问题。

远程依赖需要在repositories标签下声明远程仓库,例如jcenter()、google()、maven仓库等。

依赖配置

目前Gradle版本支持的依赖配置有:

  • implementation、
  • api、
  • compileOnly、
  • runtimeOnly
  • annotationProcessor

已经废弃的配置有:

  • compile、
  • provided、
  • apk、
  • providedCompile。

此外依赖配置还可以加一些配置项,例如AndroidTestImplementation、debugApi等等。

常用的是implementation、api、compileOnly三个依赖配置,含义如下:

implementation

与compile对应,会添加依赖到编译路径,并且会将依赖打包到输出(aar或apk),

但是在编译时不会将依赖的实现暴露给其他module,也就是只有在运行时其他module才能访问这个依赖中的实现。

使用这个配置,可以显著提升构建时间,因为它可以减少重新编译的module的数量。

建议,尽量使用这个依赖配置。

api

与compile对应,功能完全一样,会添加依赖到编译路径,并且会将依赖打包到输出(aar或apk),

与implementation不同,这个依赖可以传递

其他module无论在编译时和运行时都可以访问这个依赖的实现,也就是会泄漏一些不应该不使用的实现。

举个例子,

A依赖B,B依赖C,如果都是使用api配置的话,A可以直接使用C中的类(编译时和运行时),

而如果是使用implementation配置的话,在编译时,A是无法访问C中的类的。

compileOnly

与provided对应,

Gradle把依赖加到编译路径,编译时使用,不会打包到输出(aar或apk)。

这可以减少输出的体积,在只在编译时需要,在运行时可选的情况,很有用。

runtimeOnly

与apk对应,gradle添加依赖只打包到APK,运行时使用,但不会添加到编译路径。

这个很少使用。

annotationProcessor

与compile对应,用于注解处理器的依赖配置,

这个很少使用。

查看依赖树

可以查看单个module或者这个project的依赖,通过运行依赖的Gradle任务,如下:

  1. View -> Tools Windows -> Gradle(或者点击右侧的Gradle栏);

  2. 展开 AppName -> Tasks -> android,然后双击运行AndroidDependencies。

    运行完,就会在Run窗口打出依赖树了。

依赖冲突解决

随着很多依赖加入到项目中,难免会出现依赖冲突,出现依赖冲突如何解决?

定位冲突

依赖冲突可能会报类似下面的错误:

Program type already present com.example.MyClass

通过查找类的方式(command + O)定位到冲突的依赖,进行排除。

如何排除依赖

  • dependencies中排除(细粒度)
compile('com.taobao.android:accs-huawei:1.1.2@aar') {
        transitive = true
        exclude group: 'com.taobao.android', module: 'accs_sdk_taobao'
}
  • 全局配置排除
configurations {
    compile.exclude module: 'cglib'
    //全局排除原有的tnet jar包与so包分离的配置,统一使用aar包中的内容
    all*.exclude group: 'com.taobao.android', module: 'tnet-jni'
    all*.exclude group: 'com.taobao.android', module: 'tnet-so'
}
  • 禁用依赖传递
compile('com.zhyea:ar4j:1.0') {
    transitive = false
}
 
configurations.all {
    transitive = false
}

还可以在单个依赖项中使用@jar标识符忽略传递依赖:

compile 'com.zhyea:ar4j:1.0@jar'
  • 强制使用某个版本

如果某个依赖项是必需的,而又存在依赖冲突时,此时没必要逐个进行排除,可以使用force属性标识需要进行依赖统一。

当然这也是可以全局配置的:

compile('com.zhyea:ar4j:1.0') {
    force = true
}
 
configurations.all {
    resolutionStrategy {
        force 'org.hamcrest:hamcrest-core:1.3'
    }
}
  • 在打包时排除依赖
    先看一个示例:
task zip(type: Zip) {
    into('lib') {
        from(configurations.runtime) {
            exclude '*unwanted*', '*log*'
        }
    }
    into('') {
        from jar
        from 'doc'
    }
}

代码表示在打zip包的时候, 会过滤掉名称中包含“unwanted”和“log”的jar包。

这里调用的exclude方法的参数和前面的例子不太一样,前面的参数多是map结构,这里则是一个正则表达式字符串。
也可以使用在打包时调用include方法选择只打包某些需要的依赖项:

task zip(type: Zip) {
    into('lib') {
        from(configurations.runtime) {
            include '*ar4j*', '*spring*'
        }
    }
    into('') {
        from jar
        from 'doc'
    }
}

主要是使用dependencies中排除和全局配置排除。

Gradle使用Kotlin的init

Gradle缺省方式使用groovy作为DSL的支持方式,kotlin也作为支持方式之一,

gradle init也可以直接指定kotlin的方式生成相关的文件,使用方式如下

使用命令:gradle init --dsl kotlin

groovy 方式的构建脚本

println "[phase:configuration] build.gradle ..."

task compile {
  group 'compile'
  description 'compile task'
  println "[phase:configuration] compile"
  doFirst {
    println "[phase:execution] compile :doFirst()"
  }
}

tasks.create(name: 'test',dependsOn: compile) {
  group 'test'
  description 'test task'
  println "[phase:configuration] test"
  doLast {
    println "[phase:execution] test:doLast()"
  }
}

tasks.create("packaging") {
  group 'packaging'
  description 'packaging task'
  dependsOn test
  enabled   false
  println "[phase:configuration] packaging"
  doLast {
    println "[phase:execution] packaging:doLast()"
  }
}

class Install extends DefaultTask{
  String installObjectName

  @TaskAction
  void checkObject() {
    println "[phase:execution] install:checkObject   (${installObjectName})"
  }

  @TaskAction
  void installObject() {
    println "[phase:execution] install:installObject (${installObjectName})"
  }
}

task install(type: Install) {
  group 'install'
  description 'install task'
  installObjectName 'test.jar'

  println "[phase:configuration] install"
  doFirst {
    println "[phase:execution] install:doFirst()"
  }
  doLast {
    println "[phase:execution] install:doLast()"
  }
}
install.dependsOn packaging
install.onlyIf { packaging.enabled }

关于 本文的代码,和技术问题, 请来尼恩 发起的Java 高并发 疯狂创客圈 社群交流

kotlin方式的构建脚本

只要基本符合kotlin的语法,很容易就可以将groovy的方式改成kotlin,比如可以改成如下:

liumiaocn:kotlin liumiao$ cat build.gradle.kts 
tasks.create("compile") {
  group = "compile"
  description = "compile task"
  println("[phase:configuration] compile")
  doFirst {
    println("[phase:execution] compile :doFirst()")
  }
}

tasks.create("test") {
  group = "test"
  description = "test task"
  println("[phase:configuration] test")
  doLast {
    println("[phase:execution] test:doLast()")
  }
}.dependsOn("compile")

tasks.create("packaging") {
  group = "packaging"
  description = "packaging task"
  enabled =  true
  println("[phase:configuration] packaging")
  doLast {
    println("[phase:execution] packaging:doLast()")
  }
}.dependsOn("test")

open class Install: DefaultTask() {
  lateinit var installObjectName: String

  @TaskAction
  fun checkObject() {
    println("[phase:execution] install:checkObject   (${installObjectName})")
  }

  @TaskAction
  fun installObject() {
    println("[phase:execution] install:installObject (${installObjectName})")
  }
}

tasks.create("install") {
  group = "install"
  description = "install task"
  installObjectName = "test.jar"
  var isSkip = false

  println("[phase:configuration] install")
  doFirst {
    println("[phase:execution] install:doFirst()")
  }
  doLast {
    println("[phase:execution] install:doLast()")
  }
  onlyIf { !isSkip }
}.dependsOn("packaging")
liumiaocn:kotlin liumiao$

Kotlin 简介

Kotlin (科特林)是一个用于现代多平台应用的静态编程语言 ,由 JetBrains开发。

Kotlin可以编译成Java字节码,也可以编译成JavaScript,方便在没有JVM的设备上运行。

除此之外Kotlin还可以编译成二进制代码直接运行在机器上(例如嵌入式设备或[ iOS](https://baike.baidu.com/item/ iOS/45705?fromModule=lemma_inlink))。

Kotlin已正式成为Android官方支持开发语言。

2011年7月,JetBrains推出Kotlin项目,这是一个面向JVM的新语言,它已被开发一年之久。JetBrains负责人Dmitry Jemerov说,大多数语言没有他们正在寻找的特性,Scala除外。但是,他指出了Scala的编译时间慢这一明显缺陷。Kotlin的既定目标之一是像Java一样快速编译。 2012年2月,JetBrains以Apache 2许可证开源此项目。

Jetbrains希望这个新语言能够推动[IntelliJ IDEA](https://baike.baidu.com/item/IntelliJ IDEA/9548353?fromModule=lemma_inlink)的销售。

Kotlin v1.0于2016年2月15日发布。这被认为是第一个官方稳定版本,并且JetBrains已准备从该版本开始的长期向后兼容性。

在[Google I/O](https://baike.baidu.com/item/Google I%2FO/7108524?fromModule=lemma_inlink) 2017中,Google宣布在Android上为Kotlin提供一等支持。 [3]

设计目标

创建一种兼容Java的语言

让它比Java更安全,能够静态检测常见的陷阱。如:引用空指针

让它比Java更简洁,通过支持variable type inference,higher-order functions (closures),extension functions,mixins and first-class delegation等实现。

让它比最成熟的竞争对手Scala语言更加简单。 [3]

开放源码

其基础编译器(他们将其改为kompiler——开创了一系列以K字打头的用语——甚至连 contributors这类词他们也用改成了kontributors)可以被独立出来并嵌入到 Maven、Ant 或 Gradle 工具链中。这使得在 IDE 中开发的代码能够利用已有的机制来构建,从而尽可能的减少了在新环境中使用所受的干预,哪怕与那些没有安装 Kotlin 插件的开发人员一起合作项目也没有问题。

The IntelliJ Kotlin 插件扩展了 Java 编译器使得 Kotlin 代码能够得以编写、编译和调试。除此之外,关于基本的 Java 集合,已经有编写好的帮助函数,可以更顺畅地衔接将在 Java 8 中出现的集合扩展。

有两篇文章对 Kotlin 与 Java 以及 Kotlin 与 Scala 分别进行了比较,对各自特性和异同进行了对比。即便 Scala 可能还是更为强大些,Kotlin 还是尝试着提供比 Java 更好的函数、模式匹配、空指针预防和泛型。该语言同时也支持特征(traits)和模式匹配。

Kotlin 插件在当前版本的[ IntelliJ ](https://baike.baidu.com/item/ IntelliJ /3306058?fromModule=lemma_inlink)和 Eclipse 中均已能使用。

Gradle Gradle 插件

Gradle 在它的核心中有意地提供了一些小但有用的功能,用于在真实世界中的自动化。

所有有用的功能,例如以能够编译 Java 代码为例,都是通过插件进行添加的。

插件添加了新任务 (例如JavaCompile),域对象 (例如SourceSet),约定(例如主要的 Java 源代码是位于 src/main/java),以及扩展的核心对象和其他插件的对象。

应用插件

插件都认为是被应用,通过 Project.apply() 方法来完成。

build.gradle

apply plugin: 'java'  

插件都有表示它们自己的一个短名称。

在上述例子中,我们使用短名称 java 去应用 JavaPlugin。

我们还可以使用下面的语法:

通过类型应用插件

build.gradle

apply plugin: org.gradle.api.plugins.JavaPlugin  

由于 Gradle 的默认导入,您还可以这样写:

通过类型应用插件

build.gradle

apply plugin: JavaPlugin  

插件的应用是幂等的。

也就是说,一个插件可以被应用多次。如果以前已应用了该插件,任何进一步的应用都不会再有任何效果。

一个插件是任何实现了 Plugin 接口的简单的类。Gradle 提供了核心插件作为其发行包的一部分,所以简单地应用如上插件是你所需要做的。然而,对于第三方插件,你需要进行配置以使插件在构建类路径中可用。有关如何进行此操作的详细信息。

插件都做了什么

把插件应用到项目中可以让插件来扩展项目的功能。它可以做的事情如:

  • 将任务添加到项目 (如编译、 测试)
  • 使用有用的默认设置对已添加的任务进行预配置。
  • 向项目中添加依赖配置 (见“依赖管理基础”)。
  • 通过扩展对现有类型添加新的属性和方法。

让我们来看看:

通过插件添加任务

build.gradle

apply plugin: 'java'
task show  {
    println relativePath(compileJava.destinationDir)
    println relativePath(processResources.destinationDir)
}  

gradle -q show 的输出结果

> gradle -q show
build/classes/main
build/resources/main  

Java 插件已经向项目添加了 compileJava 任务和 processResources 任务,并且配置了这两个任务的 destinationDir 属性。

约定

插件可以通过智能的方法对项目进行预配置以支持约定优于配置。

Gradle 对此提供了机制和完善的支持,而它是强大-然而-简洁的构建脚本中的一个关键因素。

在上面的示例中我们看到,Java 插件添加了一个任务,名字为 compileJava ,有一个名为 destinationDir 的属性(即配置编译的 Java 代码存放的地方)。J

ava 插件默认此属性指向项目目录中的 build/classes/main。这是通过一个合理的默认的约定优于配置的例子。

我们可以简单地通过给它一个新的值来更改此属性。

更改插件的默认设置

build.gradle

apply plugin: 'java'
compileJava.destinationDir = file("$buildDir/output/classes")
task show  {
    println relativePath(compileJava.destinationDir)
}  

gradle -q show 的输出结果

> gradle -q show
build/output/classes  

然而,compileJava 任务很可能不是唯 一需要知道类文件在哪里的任务。

Java 插件添加了 source sets 的概念 (见SourceSet) 来描述的源文件集的各个方面,其中一个方面是在编译的时候这些类文件应该被写到哪个地方。Java 插件将 compileJava 任务的 destinationDir 属性映射到源文件集的这一个方面。

我们可以通过这个源码集修改写入类文件的位置。

插件中的约定对象

build.gradle

apply plugin: 'java'
sourceSets.main.output.classesDir = file("$buildDir/output/classes")
task show  {
    println relativePath(compileJava.destinationDir)
}  

gradle -q show 的输出结果

> gradle -q show
build/output/classes  

在上面的示例中,我们应用 Java 插件,除其他外,还做了下列操作:

  • 添加了一个新的域对象类型: SourceSet
  • 通过属性的默认(即常规)配置了 main 源码集
  • 配置支持使用这些属性来执行工作的任务

所有这一切都发生在 apply plugin: “java” 这一步过程中。在上面例子中,我们在约定配置被执行之后,修改了类文件所需的位置。在上面的示例中可以注意到,compileJava.destinationDir 的值也被修改了,以反映出配置的修改。

考虑一下另一种消费类文件的任务的情况。如果这个任务使用 sourceSets.main.output.classesDir 的值来配置,那么修改了这个位置的值,无论它是什么时候被修改,将同时更新 compileJava 任务和这一个消费者任务。

这种配置对象的属性以在所有时间内(甚至当它更改的时候)反映另一个对象的任务的值的能力被称为“映射约定”。它可以令 Gradle 通过约定优于配置及合理的默认值来实现简洁的配置方式。而且,如果默认约定需要进行修改时,也不需要进行完全的重新配置。如果没有这一点,在上面的例子中,我们将不得不重新配置需要使用类文件的每个对象。

关于 本文的代码,和技术问题, 请来尼恩 发起的Java 高并发 疯狂创客圈 社群交流

Gradle 标准的 Gradle 插件

Gradle 的发行包中有大量的插件。如下列所示:

语言插件

这些插件添加了让各种语言可以被编译和在 JVM 执行的支持。

语言插件

插件 Id 自动应用 与什么插件一起使用 描述
java java-base - 向一个项目添加 Java 编译、 测试和捆绑的能力。它是很多其他 Gradle 插件的基础服务。
groovy groovy-base - 添加对 Groovy 项目构建的支持。
scala scala-base - 添加对 Scala 项目构建的支持。
antlr java - 添加对使用Antlr作为生成解析器的支持。

正在孵化的语言插件

这些插件添加了对多种语言的支持:

插件 Id 自动应用 与什么插件一起使用 描述
assembler - - 向项目添加本机汇编语言的功能。
c - - 向项目添加 C语言源代码编译功能。
cpp - - 向项目添加 c++ 源代码编译功能。
objective-c - - 向项目中添加 Objective-C 源代码编译功能。
objective-cpp - - 向项目中添加 Objective-C++ 源代码编译功能。
windows-resources - - 添加对在本地bin文件中包含 Windows 资源的支持。

集成插件

以下这些插件提供了一些与各种运行时技术的集成。

插件 Id 自动应用 与什么插件一起使用 描述
application java - 添加了一些任务,用于运行和捆绑一个Java项目作为命令行应用程序。
ear - java 添加用于构建 J2EE 应用程序的支持。
jetty war - 在构建中部署你的web程序到一个内嵌的Jetty web容器中。
maven - war 添加用于将项目发布到 Maven 仓库的支持。
osgi java-base java 添加构建 OSGi 捆绑包的支持。
war java - 添加用于组装 web 应用程序的 WAR 文件的支持。

孵化中的集成插件

以下这些插件提供了一些与各种运行时技术的集成。

孵化中的集成插件

插件 Id 自动应用 与什么插件一起使用 描述
distribution - - 添加构建 ZIP 和 TAR 分发包的支持。
java-library-distribution distribution - 添加构建一个Java类库的 ZIP 和 TAR 分发包的支持。
ivy-publish - war 这个插件提供了新的 DSL,用于支持发布文件到 Ivy 存储库,改善了现有的 DSL。
maven-publish - war 这个插件提供了新的 DSL,用于支持发布文件到 Maven 存储库,改善了现有的 DSL。

软件开发插件

这些插件提供一些软件开发过程上的帮助。

插件 Id 自动应用 与什么插件一起使用 描述
announce - - 将消息发布到你所喜爱的平台,如 Twitter 或 Growl。
build-announcements announce - 在构建的生命周期中,把本地公告中有关你感兴趣的事件发送到你的桌面。
checkstyle java-base - 使用Checkstyle对您的项目的 Java 源文件执行质量检查并生成报告。
codenarc groovy-base - 使用CodeNarc对您的项目的 Groovy 源文件执行质量检查并生成报告。
eclipse - scala 生成Eclipse IDE所用到的文件,从而使项目能够导入到 Eclipse。
eclipse-wtp - war 与 eclipse 插件一样,但它还生成 eclipse WTP (Web 工具平台) 的配置文件。你的war/ear项目在导入eclipse 后,应配置为能在 WTP 中使用。
findbugs java-base - 使用FindBugs对您的项目的 Java 源文件执行质量检查并生成报告。
idea - java 生成Intellij IDEA IDE所用到的文件,从而使项目能够导入到 IDEA。
jdepend java-base - 使用JDepend对您的项目的源文件执行质量检查并生成报告。
pmd java-base - 使用PMD对您的项目的 Java 源文件执行质量检查并生成报告。
project-report reporting-base - 生成关于Gradle构建中有用的信息的报告。
signing base - 添加对生成的文件或构件进行数字签名的功能。
sonar - java-base, java, jacoco 提供对sonar-runner插件取代。

孵化中的软件开发插件

这些插件提供一些软件开发过程上的帮助。

插件 Id 自动应用 与什么插件一起使用 描述
build-dashboard reporting-base - 生成构建的主控面板的报表。
build-init wrapper - 添加用于初始化一个新 Gradle 构建的支持。处理转换 Maven 构建为 Gradle 构建。
cunit - - 添加用于运行CUnit测试的支持。
jacoco reporting-base java 提供对 Java 的JaCoCo代码覆盖率库的集成。
sonar-runner - java-base, java, jacoco 提供对sonar插件取代。
visual-studio - 本机语言插件 添加对 Visual Studio 的集成。
wrapper - - 添加一个用于生成 Gradle wrapper 文件的Wrapper任务。

基本插件

这些插件组成了基本的构建块,其他插件都由此组装而来。它们可供你在你的构建文件中使用,并在此处完整列出。然而,请注意它们都不被认为是 Gradle 公共 API 的一部分。因此,这些插件都不在用户指南中记录。您可能会引用他们的 API 文档,以了解更多关于它们的信息。

插件 Id 描述
base 添加标准的生命周期任务,并为归档任务默认进行合理的配置:添加构建 添加上传 为所有归档任务配置合适的默认值(比如从version属性被预先配置了默认值,这是非常有用的,因为它促进了跨项目的一致性 ;完成了有关构件命名规范及构建之后的位置上的一致。)
java-base 对项目添加source set 的概念。不会添加任何特定的soruce sets。
groovy-base 向项目中添加Groovy 的source set概念。
scala-base 向项目中添加Scala 的source set概念。
reporting-base 将一些共享的公约属性添加到项目中,它们与报告的生成有关。

Gradle Java 插件

Java 插件向一个项目添加了 Java 编译、 测试和 bundling 的能力。它是很多其他 Gradle 插件的基础服务。

用法

要使用 Java 插件,请在构建脚本中加入:

使用 Java 插件

build.gradle

apply plugin: 'java'  

源集

Java 插件引入了一个源集的概念。一个源集只是一组用于编译并一起执行的源文件。这些源文件可能包括 Java 源代码文件和资源文件。其他有一些插件添加了在源集里包含 Groovy 和 Scala 的源代码文件的能力。一个源集有一个相关联的编译类路径和运行时类路径。

源集的一个用途是,把源文件进行逻辑上的分组,以描述它们的目的。例如,你可能会使用一个源集来定义一个集成测试套件,或者你可能会使用单独的源集来定义你的项目的 API 和实现类。

Java 插件定义了两个标准的源集,分别是 main 和 test。main 源集包含你产品的源代码,它们将被编译并组装成一个 JAR 文件。test 源集包含你的单元测试的源代码,它们将被编译并使用 JUnit 或 TestNG 来执行。

任务

Java 插件向你的项目添加了大量的任务,如下所示。

表 23.1. Java 插件-任务

任务名称 依赖于 类型 描述
compileJava 产生编译类路径中的所有任务。这包括了用于jar任务。 JavaCompile 使用 javac 编译产品中的 Java 源文件。
processResources - Copy 把生产资源文件拷贝到生产的类目录中。
classes processResources。一些插件添加了额外的编译任务。 Task 组装生产的类目录。
compileTestJava compile,再加上所有能产生测试编译类路径的任务。 JavaCompile 使用 javac 编译 Java 的测试源文件。
processTestResources - Copy 把测试的资源文件拷贝到测试的类目录中。
testClasses processTestResources。一些插件添加了额外的测试编译任务。 Task 组装测试的类目录。
jar compile Jar 组装 JAR 文件
javadoc compile Javadoc 使用 Javadoc 生成生产的 Java 源代码的API文档
test compileTest,再加上所有产生测试运行时类路径的任务。 Test 使用 JUnit 或 TestNG运行单元测试。
uploadArchives 使用jar Upload 使用archives配置上传包括 JAR 文件的构件。
clean - Delete 删除项目的 build 目录。
TaskName - Delete 删除由指定的任务所产生的输出文件。例如, jar任务中所创建的 JAR 文件,test任务所创建的测试结果。

对于每个你添加到该项目中的源集,Java 插件将添加以下的编译任务:

表 23.2. Java 插件-源集任务

任务名称 依赖于 类型 描述
SourceSetJava 所有产生源集编译类路径的任务。 JavaCompile 使用 javac 编译给定的源集中的 Java 源文件。
SourceSetResources - Copy 把给定的源集的资源文件拷贝到类目录中。
sourceSetClasses SourceSetResources。某些插件还为源集添加了额外的编译任务。 Task 组装给定源集的类目录。

Java 插件还增加了大量的任务构成该项目的生命周期:

表 23.3. Java 插件-生命周期任务

任务名称 依赖于 类型 描述
assemble 项目中的所有归档项目,包括jar任务。某些插件还向项目添加额外的归档任务。 Task 组装项目中所有的归类文件。
check 项目中的所有核查项目,包括test任务。某些插件还向项目添加额外的核查任务。 Task 执行项目中所有的核查任务。
build assemble Task 执行项目的完事构建。
buildNeeded build任务。 Task 执行项目本身及它所依赖的其他所有项目的完整构建。
buildDependents build任务。 Task 执行项目本身及依赖它的其他所有项目的完整构建。
ConfigurationName 使用配置ConfigurationName生成构件的任务。 Task 组装指定配置的构件。该任务由Base插件添加,并由Java插件隐式实现。
ConfigurationName 使用配置ConfigurationName上传构件的任务。 Upload 组装并上传指定配置的构件。该任务由Base插件添加,并由Java插件隐式实现。

uploadConfigurationName 使用配置 ConfigurationName 上传构件的任务。 Upload 组装并上传指定配置的构件。该任务由 Base 插件添加,并由 Java 插件隐式实现。 下图显示了这些任务之间的关系。

图23.1. Java 插件 - 任务

Java 插件 - 任务

项目布局

Java 插件会假定如下所示的项目布局。这些目录都不需要一定存在,或者是里面有什么内容。Java 插件将会进行编译,不管它发现什么,并处理缺少的任何东西。

表 23.4. Java 插件-默认项目布局

目录 意义
src/main/java 产品的Java源代码
src/main/resources 产品的资源
src/test/java Java 测试源代码
src/test/resources 测试资源
sourceSet/java 给定的源集的Java源代码
sourceSet/resources 给定的源集的资源

更改项目布局

你可以通过配置适当的源集,来配置项目的布局。这一点将在以下各节中详细讨论。这里是如何更改 main Java 和资源源目录的一个简短的例子。

自定义 Java 源代码布局

build.gradle

sourceSets {
    main {
        java {
            srcDir 'src/java'
        }
        resources {
            srcDir 'src/resources'
        }
    }
}  

依赖管理

Java 插件向项目添加了许多依赖配置,如下图所示。它对一些任务指定了这些配置,如 compileJava 和 test。

表23.5. Java插件 - 依赖配置

名称 继承自 在哪些任务中使用 意义
compile - compileJava 编译时依赖
runtime compile - 运行时依赖
testCompile compile compileTestJava 用于编译测试的其他依赖
testRuntime runtime, testCompile test 只用于运行测试的其他依赖
archives - uploadArchives 由本项目生产的构件(如jar包)。
default runtime - 本项目上的默认项目依赖配置。包含本项目运行时所需要的构件和依赖。

图23.2. Java 插件 - 依赖配置

Java 插件 - 依赖配置

对于每个你添加到项目中的源集,Java 插件都会添加以下的依赖配置:

表23.6. Java 插件 - 源集依赖配置

名称 继承自 在哪些任务中使用 意义
sourceSetCompile - compileSourceSetJava 给定源集的编译时依赖
sourceSetRuntime sourceSetCompile - 给定源集的运行时依赖

常规属性

Java 插件向项目添加了许多常规属性,如下图所示。您可以在构建脚本中使用这些属性,就像它们是 project 对象的属性一样。

表23.7. Java 插件 - 目录属性

属性名称 类型 默认值 描述
reportsDirName String reports 相对于build目录的目录名称,报告将生成到此目录。
reportsDir File (read-only) reportsDirName 报告将生成到此目录。
testResultsDirName String test-results 相对于build目录的目录名称,测试报告的.xml文件将生成到此目录。
testResultsDir File (read-only) testResultsDirName 测试报告的.xml文件将生成到此目录。
testReportDirName String tests 相对于build目录的目录名称,测试报告将生成到此目录。
testReportDir File (read-only) testReportDirName 测试报告生成到此目录。
libsDirName String libs 相对于build目录的目录名称,类库将生成到此目录中。
libsDir File (read-only) libsDirName 类库将生成到此目录中。
distsDirName String distributions 相对于build目录的目录名称,发布的文件将生成到此目录中。
distsDir File (read-only) distsDirName 要发布的文件将生成到此目录。
docsDirName String docs 相对于build目录的目录名称,文档将生成到此目录。
docsDir File (read-only) docsDirName 要生成文档的目录。
dependencyCacheDirName String dependency-cache 相对于build目录的目录名称,该目录用于缓存源代码的依赖信息。
dependencyCacheDir File (read-only) dependencyCacheDirName 该目录用于缓存源代码的依赖信息。

表 23.8. Java 插件 - 其他属性

属性名称 类型 默认值 描述
sourceSets SourceSetContainer (read-only) 非空 包含项目的源集。
sourceCompatibility JavaVersion. 可以使用字符串或数字来设置,例如1.5 当前JVM所使用的值 当编译Java源代码时所使用的Java版本兼容性。
targetCompatibility JavaVersion. 可以使用字符串或数字来设置,例如1.5 sourceCompatibility 要生成的类的 Java 版本。
archivesBaseName String projectName 像JAR或ZIP文件这样的构件的basename
manifest Manifest 一个空的清单 要包括的所有 JAR 文件的清单。

这些属性由 JavaPluginConvention, BasePluginConvention 和 ReportingBasePluginConvention 这些类型的常规对象提供。

使用源集

你可以使用 sourceSets 属性访问项目的源集。这是项目的源集的容器,它的类型是 SourceSetContainer。除此之后,还有一个 sourceSets {}的脚本块,可以传入一个闭包来配置源集容器。源集容器的使用方式几乎与其他容器一样,例如 tasks。

访问源集

build.gradle

// Various ways to access the main source set
println sourceSets.main.output.classesDir
println sourceSets['main'].output.classesDir
sourceSets {
    println main.output.classesDir
}
sourceSets {
    main {
        println output.classesDir
    }
}
// Iterate over the source sets
sourceSets.all {
    println name
}  

要配置一个现有的源集,你只需使用上面的其中一种访问方法来设置源集的属性。这些属性将在下文中进行介绍。下面是一个配置 main 的 Java 和资源目录的例子:

配置源集的源代码目录

build.gradle

sourceSets {
    main {
        java {
            srcDir 'src/java'
        }
        resources {
            srcDir 'src/resources'
        }
    }
}  

源集属性

下表列出了一些重要的源集属性。你可以在 SourceSet 的 API 文档中查看更多的详细信息。

表 23.9. Java 插件 - 源集属性

属性名称 类型 默认值 描述
name String (read-only) 非空 用来确定一个源集的源集名称。
output SourceSetOutput (read-only) 非空 源集的输出文件,包含它编译过的类和资源。
output.classesDir File name 要生成的该源集的类的目录。
output.resourcesDir File name 要生成的该源集的资源的目录。
compileClasspath FileCollection SourceSet 配置。 该类路径在编译该源集的源文件时使用。
runtimeClasspath FileCollection SourceSet 配置。 该类路径在执行该源集的类时使用。
java SourceDirectorySet (read-only) 非空 该源集的Java源文件。仅包含Java源文件目录里的.java文件,并排除其他所有文件。
java.srcDirs Set name/java] 该源目录包含了此源集的所有Java源文件。
resources SourceDirectorySet (read-only) 非空 此源集的资源文件。仅包含资源文件,并且排除在资源源目录中找到的所有 .java文件。其他插件,如Groovy 插件,会从该集合中排除其他类型的文件。
resources.srcDirs Set name/resources] 该源目录包含了此源集的资源文件。
allJava SourceDirectorySet (read-only) java 该源集的所有.java 文件。有些插件,如Groovy 插件,会从该集合中增加其他的Java源文件。
allSource SourceDirectorySet (read-only) resources + java 该源集的所有源文件。包含所有的资源文件和Java源文件。有些插件,如Groovy 插件,会从该集合中增加其他的源文件。

定义新的源集

要定义一个新的源集,你只需在 sourceSets {}块中引用它。下面是一个示例:

定义一个源集

build.gradle

sourceSets {
    intTest
}  

当您定义一个新的源集时,Java 插件会为该源集添加一些依赖配置,如表 23.6,“Java 插件 - 源集依赖项配置”所示。你可以使用这些配置来定义源集的编译和运行时的依赖。

定义源集依赖

build.gradle

sourceSets {
    intTest
}
dependencies {
    intTestCompile 'junit:junit:4.11'
    intTestRuntime 'org.ow2.asm:asm-all:4.0'
}  

Java 插件还添加了大量的任务,用于组装源集的类,如表 23.2,“Java 插件 - 源设置任务”所示。例如,对于一个被叫做 intTest 的源集,你可以运行 gradle intTestClasses 来编译 int 测试类。

编译源集

gradle intTestClasses的输出结果

> gradle intTestClasses
:compileIntTestJava
:processIntTestResources
:intTestClasses
BUILD SUCCESSFUL
Total time: 1 secs  

一些源集的范例

添加一个包含了源集的类的 JAR 包

示例 23.8. 为一个源集装配一个JAR文件

build.gradle

task intTestJar(type: Jar) {
    from sourceSets.intTest.output
}  

为一个源集生成 Javadoc:

示例 23.9. 为一个源集生成 Javadoc:

build.gradle

task intTestJavadoc(type: Javadoc) {
    source sourceSets.intTest.allJava
}  

添加一个测试套件以运行一个源集里的测试

示例 23.10. 运行源集里的测试

build.gradle

task intTest(type: Test) {
    testClassesDir = sourceSets.intTest.output.classesDir
    classpath = sourceSets.intTest.runtimeClasspath
}  

Javadoc

Javadoc 任务是 Javadoc 的一个实例。

它支持核心的 javadoc 参数选项,以及在 Javadoc 可执行文件的参考文档中描述的标准 doclet 参数选项。

对于支持的 Javadoc 参数选项的完整列表,

请参考下面的类的 API 文档: CoreJavadocOptions 和StandardJavadocDocletOptions。

表 23.10. Java 插件 - Javadoc 属性

任务属性 类型 默认值
classpath FileCollection sourceSets.main.output + sourceSets.main.compileClasspath
source FileTree. sourceSets.main.allJava
destinationDir File docsDir/javadoc
title String project的名称和版本

清理

clean 任务是 Delete 的一个实例。它只是删除由其 dir 属性表示的目录。

表 23.11. Java 插件 - Clean 性能

任务属性 类型 默认值
dir File buildDir

资源

Java 插件使用 Copy 任务进行资源的处理。它为该 project 里的每个源集添加一个实例。你可以在16.6章节,“复制文件”中找到关于 copy 任务的更多信息。

表 23.12. Java 插件-ProcessResources 属性

任务属性 类型 默认值
srcDirs Object. sourceSet.resources
destinationDir 16.1 章节,“查找文件”中所讲到的任何一种方式来设置。 sourceSet.output.resourcesDir

CompileJava

Java 插件为该 project 里的每个源集添加一个 JavaCompile 实例。一些最常见的配置选项如下所示。

表 23.13. Java 插件- Compile 属性

任务属性 类型 默认值
classpath FileCollection sourceSet.compileClasspath
source FileTree sourceSet.java
destinationDir File. sourceSet.output.classesDir

compile 任务委派了 Ant 的 javac 任务。将 options.useAnt 设置为 false 将绕过 Ant 任务,而激活 Gradle 的直接编译器集成。在未来的 Gradle 版本中,将把它作为默认设置。

默认情况下,Java 编译器在 Gradle 过程中运行。将 options.fork 设置为 true 将会使编译出现在一个单独的进程中。在 Ant javac 任务中,这意味着将会为每一个 compile 任务fork 一个新的进程,而这将会使编译变慢。相反,Gradle 的直接编译器集成 (见上文) 将尽可能多地重用相同的编译器进程。在这两种情况下,使用 options.forkOptions 指定的所有fork 选项都将得到重视。

Test

test 任务是 Test 的一个实例。它会自动检测和执行 test 源集中的所有单元测试。测试执行完成后,它还会生成一份报告。同时支持 JUnit 和 TestNG。可以看一看Test的完整的 API。

测试执行

测试在单独的 JVM 中执行,与 main 构建进程隔离。Test 任务的 API 可以让你控制什么时候开始。

有大量的属性用于控制测试进程的启动。这包括系统属性、 JVM 参数和使用的 Java 可执行文件。

你可以指定是否要并行运行你的测试。Gradle 通过同时运行多个测试进程来提供并行测试的执行。每个测试进程会依次执行一个单一的测试,所以你一般不需要对你的测试做任何的配置来利用这一点。 MaxParallelForks 属性指定在给定的时间可以运行的测试进程的最大数。它的默认值是 1,也就是说,不并行执行测试。

测试进程程会为其将 org.gradle.test.worker 系统属性设置为一个唯一标识符,这个标识符可以用于文件名称或其他资源标识符。

你可以指定在执行了一定数量的测试类之后,重启那个测试进程。这是一个很有用的替代方案,让你的测试进程可以有很大的堆内存。forkEvery 属性指定了要在测试进程中执行的测试类的最大数。默认是每个测试进程中执行的测试数量不限。

该任务有一个 ignoreFailures 属性,用以控制测试失败时的行为。test 会始终执行它检测到的每一个测试。如果 ignoreFailures 为 false,并且有测试不通过,那它会停止继续构建。IgnoreFailures 的默认值为 false。

testLogging 属性可以配置哪些测试事件需要记录,并且使用什么样的日志级别。默认情况下,对于每个失败的测试都只会打印一个简洁的消息。请参阅 TestLoggingContainer,查看如何把你的测试日志打印调整为你的偏好设置。

调试

test 任务提供了一个 Test.getDebug()属性,可以设置为启动,使 JVM 在执行测试之前,等待一个 debugger 连接到它的 5005 端口上。

这也可以在调用时通过–debug-vm task 选项进行启用。

测试过滤

从 Gradle 1.10 开始,可以根据测试的名称模式,只包含指定的测试。过滤,相对于测试类的包含或排除,是一个不同的机制。它将在接下来的段落中进行描述(-Dtest.single, test.include 和 friends)。后者基于文件,比如测试实现类的物理位置。file-level 的测试选择不支持的很多有趣的情况,都可以用 test-level 过滤来做到。以下这些场景中,有一些 Gradle 现在就可以处理,而有一些则将在未来得到实现:

  • 在指定的测试的方法级别上进行过滤;执行单个测试方法
  • 基于自定义注解(以后实现)进行过滤
  • 基于测试层次结构进行过滤 ;执行所有继承了某一基类(以后实现) 的测试
  • 基于一些自定义的运行时的规则进行过滤,例如某个系统属性或一些静态的特定值(以后实现)

测试过滤功能具有以下的特征:

  • 支持完整的限定类名称或完整的限定的方法名称,例如“org.gradle.SomeTest”、“org.gradle.SomeTest.someMethod”
  • 通配符 “*” 支付匹配任何字符
  • 提供了“–tests”的命令行选项,以方便地设置测试过滤器。这对“单一测试方法的执行”的经典用例特别有用。当使用该命令行选项选项时,在构建脚本中声明的列入过滤器的测试将会被忽略。
  • Gradle 尽最大努力对有着特定的测试框架 API 的局限的测试进行过滤。一些高级的、 综合的测试可能不完全符合过滤条件。然而,绝大多数的测试和用例都会被很好地处理。
  • 测试过滤将会取代基于文件的测试选择。后者可能在将来会被完全地取代掉。我们将会继续改进测试过滤的 API,并添加更多种类的过滤器。

在构建脚本中过滤测试

build.gradle

test {
    filter {
        //include specific method in any of the tests
        includeTestsMatching "*UiCheck"
        //include all tests from package
        includeTestsMatching "org.gradle.internal.*"
        //include all integration tests
        includeTestsMatching "*IntegTest"
    }
}  

有关更多的详细信息和示例,请参阅 TestFilter 的文档。

使用命令行选项的一些示例:

  • gradle test --tests org.gradle.SomeTest.someSpecificFeature
  • gradle test --tests *SomeTest.someSpecificFeature
  • gradle test --tests *SomeSpecificTest
  • gradle test --tests all.in.specific.package*
  • gradle test --tests *IntegTest
  • gradle test --tests IntegTestui*
  • gradle someTestTask --tests UiTest someOtherTestTask --tests WebTest*ui

通过系统属性执行单一的测试

这种机制已经被上述的“测试过滤”所取代。 设置一个 taskName.single = testNamePattern 的系统属性将会只执行匹配 testNamePattern 的那些测试。这个 taskName 可以是一个完整的多项目路径,比如“sub1:sub2:test”,或者仅是一个任务名称。testNamePattern 将用于形成一个“**/testNamePattern*.class” 的包含模式。如果这个模式无法找到任何测试,那么将会抛出一个异常。这是为了使你不会误以为测试通过。如果执行了一个以上的子项目的测试,该模式会被应用于每一个子项目中。如果在一个特定的子项目中,找不到测试用例,那么将会抛出异常。在这种情况下你可以使用路径标记法的模式,这样该模式就会只应用于特定的子项目的测试任务中。或者你可以指定要执行的任务的完整限定名称。你还可以指定多个模式。示例:

  • gradle -Dtest.single=ThisUniquelyNamedTest test
  • gradle -Dtest.single=a/b/ test
  • gradle -DintegTest.single=*IntegrationTest integTest
  • gradle -Dtest.single=:proj1:test:Customer build
  • gradle -DintegTest.single=c/d/ :proj1:integTest

测试检测

Test 任务通过检查编译过的测试类来检测哪些类是测试类。默认情况下它会扫描所有的.class 文件。您可以设置自定义的 includes 或 excludes,这样就只有这些类才会被扫描。根据所使用的测试框架 (JUnit 或 TestNG),会使用不同的标准进行测试类的检测。

当使用 JUnit 时,我们扫描 JUnit 3 和 4 的测试类。如果满足以下的任何一个条件,这个类就被认为是一个 JUnit 测试类:

  • 类或超类继承自 TestCase 或 GroovyTestCase
  • 类或超类使用了 @RunWith 进行注解
  • 类或超类含有一个带 @Test 注解的方法

当使用 TestNG 时,我们扫描所有带有 @Test 注解的方法。

请注意,抽象类不会被执行。Gradle 还将扫描测试类路径中的 jar 文件里的继承树。

如果你不想要使用测试类检测,可以通过设置 scanForTestClasses 为 false 来禁用它。这将使test任务只使用 includes / excludes 来找到测试类。如果 scanForTestClasses 为disabled,并且没有指定 include 或 exclude 模式,则使用各自的默认值。对于 include 的默认值是 “/*Tests.class", “*/Test.class”,而对于exclude它的默认值是 "/Abstract*.class”。

测试分组

JUnit 和 TestNG 可以对测试方法进行复杂的分组。

为对 Junit 测试类和方法进行分组,JUnit 4.8 引入了类别的概念。test 任务可以实现一个规范,让你 include 和 exclude 想要的 JUnit 类别。

JUnit 类别

build.gradle

test {
    useJUnit {
        includeCategories 'org.gradle.junit.CategoryA'
        excludeCategories 'org.gradle.junit.CategoryB'
    }
}  

TestNG 框架有一个非常相似的概念。在 TestNG 中你可以指定不同的测试组。应从测试执行中 include 或 exclude 的测试组,可以在 test 任务中配置。

对 TestNG 测试分组

build.gradle

test {
    useTestNG {
        excludeGroups 'integrationTests'
        includeGroups 'unitTests'
    }
}  

测试报告

test 任务默认情况下会生成以下结果。

  • 一个 HTML 测试报告。
  • 与 Ant Junit report 任务兼容的 XML 格式的结果。这种格式可以被许多工具所支持,比如CI服务器。
  • 有效二进制格式的结果。这个任务会从这些二进制结果生成其他的结果。

您可以使用 Test.setTestReport()方法来禁用 HTML 测试报告。目前不能禁用其他的结果。

这里还有一个独立的 TestReport 任务类型,它可以从一个或多个 Test 任务实例生成的二进制结果中生成 HTML 测试报告。要使用这个任务类型,你需要定义一个 destinationDir 和要包含到报告的测试结果。这里是一个范例,从子项目的单元测试中生成一个联合报告:

为多个子项目创建一个单元测试报告

build.gradle

subprojects {
    apply plugin: 'java'

    // Disable the test report for the individual test task
    test {
        reports.html.enabled = false
    }
}
task testReport(type: TestReport) {
    destinationDir = file("$buildDir/reports/allTests")
    // Include the results from the `test` task in all subprojects
    reportOn subprojects*.test
}  

你应该注意到,TestReport 类型组合了多个测试任务的结果,并且需要聚合各个测试类的结果。这意味着,如果一个给定的测试类被多个 test 任务所执行,那么测试报告将包括那个类的所有执行结果,但它难以区分那个类的每次执行和它们的输出。

TestNG 参数化方法和报告

TestNG 支持参数化测试方法,允许一个特定的测试方法使用不同的输入执行多次。Gradle 会在这个方法的执行报告中包含进它的参数值。

给定一个带有两个参数,名为 aParameterizedTestMethod 参数化测试方法,它将使用名称这个名称进行报告 :aParameterizedTestMethod(toStringValueOfParam1, toStringValueOfParam2)。这使得在特定的迭代过程,很容易确定参数值。

常规值

表 23.14. Java 插件 - test 属性

任务属性 类型 默认值
testClassesDir File sourceSets.test.output.classesDir
classpath FileCollection sourceSets.test.runtimeClasspath
testResultsDir File testResultsDir
testReportDir File testReportDir
testSrcDirs List sourceSets.test.java.srcDirs

Jar

Jar 任务创建一个包含类文件和项目资源的 JAR 文件。JAR 文件在 archives 依赖配置中被声明为一个构件。这意味着这个 JAR 文件被包含在一个依赖它的项目的类路径中。如果你把你的项目上传到仓库,这个 JAR 文件会被声明为依赖描述符的一部分。你可以在第16.8节,“创建档案”和第51章, 发布 artifact 中了解如何使用 archives 和配置 artifact。

Manifest

每个 jar 或 war 对象都有一个单独的 Manifest 实例的 manifest 属性。当生成 archive 时,相应的 MANIFEST.MF 文件也会被写入进去。

自定义的 MANIFEST.MF

build.gradle

jar {
    manifest {
        attributes("Implementation-Title": "Gradle", "Implementation-Version": version)
    }
}  

您可以创建一个单独的 Manifest 实例。它可以用于共享两个 jar 包的 manifest 信息。

创建一个 manifest 对象。

build.gradle

ext.sharedManifest = manifest {
    attributes("Implementation-Title": "Gradle", "Implementation-Version": version)
}
task fooJar(type: Jar) {
    manifest = project.manifest {
        from sharedManifest
    }
}  

你可以把其他的 manifest 合并到任何一个 Manifest 对象中。其他的 manifest 可能使用文件路径来描述,像上面的例子,使用对另一个 Manifest 对象的引用。

指定 archive 的单独 MANIFEST.MF

build.gradle

task barJar(type: Jar) {
    manifest {
        attributes key1: 'value1'
        from sharedManifest, 'src/config/basemanifest.txt'
        from('src/config/javabasemanifest.txt', 'src/config/libbasemanifest.txt') {
            eachEntry { details ->
                if (details.baseValue != details.mergeValue) {
                    details.value = baseValue
                }
                if (details.key == 'foo') {
                    details.exclude()
                }
            }
        }
    }
}  

Manifest 会根据在 from 语句中所声明的顺序进行合并。如果基本的 manifest 和要合并的 manifest 都定义了同一个 key 的值,那么默认情况下会采用要合并的 manifest 的值。你可以通过添加 eachEntry action 来完全自定义合并行为,它可以让你对每一项生成的 manifest 访问它的一个 ManifestMergeDetails 实例。这个合并操作不会在 from 语句中就马上被触发。它是懒执行的,不管是对于生成一个 jar 包,还是调用了 writeTo 或者 effectiveManifest

你可以很轻松地把一个 manifest 写入磁盘中。

指定 archive 的单独 MANIFEST.MF

build.gradle

jar.manifest.writeTo("$buildDir/mymanifest.mf")  

参考

  1. 疯狂创客圈 JAVA 高并发 总目录
    https://www.cnblogs.com/crazymakercircle/p/9904544.html
    ThreadLocal(史上最全)
    https://www.cnblogs.com/crazymakercircle/p/14491965.html

  2. 3000页《尼恩 Java 面试宝典 》的 35个面试专题 :
    https://www.cnblogs.com/crazymakercircle/p/13917138.html

  3. 价值10W的架构师知识图谱
    https://www.processon.com/view/link/60fb9421637689719d246739

4、架构师哲学
https://www.processon.com/view/link/616f801963768961e9d9aec8

5、尼恩 3高架构知识宇宙
https://www.processon.com/view/link/635097d2e0b34d40be778ab4

Kotlin 1.4 现已发布,专注于质量和性能 – Kotlin Blog | JetBrains .Kotlin官网.2020-08-17[引用日期2020-08-29]

6 Kotlin Programming Language .Kotlin[引用日期2018-02-02]

7 基于JVM的编程语言 Kotlin .开源社区网[引用日期2012-08-16]

8 IDEA下搭建Kotlin环境 .CSDN[引用日期2020-07-09]

9 Eclipse下搭建Kotlin开发环境 .CSDN.2016-02-18[引用日期2017-11-15]

10 Kotlin Programming Language .Kotlin语言官网[引用日期2021-02-22]

https://www.zhangshilong.cn/work/24556.html

https://blog.csdn.net/qq_41895810/article/details/120042035

https://blog.csdn.net/shuair/article/details/107319204

https://www.cnblogs.com/albertdale/p/14939529.html

https://blog.csdn.net/ywb201314/article/details/125716928

https://blog.csdn.net/qq_23085893/article/details/79633934

你可能感兴趣的:(java,java,gradle)