文章很长,而且持续更新,建议收藏起来,慢慢读!疯狂创客圈总目录 博客园版 为您奉上珍贵的学习资源 :
对于用惯了 maven的人来说, Graddle不好用, 非常不好用, 主要是环境 会遇到各种各样的问题
但是,越来越多的 场景使用到了 Graddle,但是
spring 的源码,使用 Gradle 构建
elasticsearch 的源码,使用 Gradle 构建
caffeine 的源码,使用 Gradle 构建
所以 Gradle 的文章,来了
关于 本文的代码,和技术问题, 请来尼恩 发起的Java 高并发 疯狂创客圈 社群交流 ,
Gradle是一个基于Apache Ant和Apache Maven概念的项目自动化构建开源工具。它使用一种基于Groovy的特定领域语言(DSL)来声明项目设置,也增加了基于Kotlin语言的kotlin-based DSL,抛弃了基于XML的各种繁琐配置。
面向Java应用为主。当前其支持的语言C++、Java、Groovy、Kotlin、Scala和Swift,计划未来将支持更多的语言。
projects 和 tasks是 Gradle 中最重要的两个概念。
任何一个 Gradle 构建都是由一个或多个 projects 组成。
每个 project 包括许多可构建组成部分。 这完全取决于你要构建些什么。
举个例子,每个 project 或许是一个 jar 包或者一个 web 应用,它也可以是一个由许多其他项目中产生的 jar 构成的 zip 压缩包。一个 project 不必描述它只能进行构建操作。它也可以部署你的应用或搭建你的环境。
不要担心它像听上去的那样庞大。 Gradle 的 build-by-convention 可以让您来具体定义一个 project 到底该做什么。
每个 project 都由多个 tasks 组成。
每个 task 都代表了构建执行过程中的一个原子性操作。如编译,打包,生成 javadoc,发布到某个仓库等操作。
你可以通过在命令行运行 gradle 命令来执行构建,gradle 命令会从当前目录下寻找 build.gradle 文件来执行构建。我们称 build.gradle 文件为构建脚本。
严格来说这其实是一个构建配置脚本,后面你会了解到这个构建脚本定义了一个 project 和一些默认的 task。
要尝试这一点,请创建以下名为 build.gradle 的构建脚本。
build.gradle
task hello {
doLast {
println 'Hello world!'
}
}
关于 本文的代码,和技术问题, 请来尼恩 发起的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"
}
关于 本文的代码,和技术问题, 请来尼恩 发起的Java 高并发 疯狂创客圈 社群交流 ,
build.gradle
task myTask {
ext.myProperty = "myValue"
}
task printTaskProperties << {
println myTask.myProperty
}
关于 本文的代码,和技术问题, 请来尼恩 发起的Java 高并发 疯狂创客圈 社群交流
Gradle作为基于Groovy语言的构建工具,与Gradle相比,ant显得冗长复杂,maven显得有些局促落后,但Gradle基于DSL语法,特点明显。 简洁、灵活、可读性强
1 .基本构成信息
maven使用. pom文件,
gradle使用. gradle文件基于XML,gradle使用Groovy,gradle使用maven需要固定配置的groupId、artfactId、软件包等基本参数
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的配置命令
在实际使用中,大多数情况下,需要管理的不仅仅是一个项目。 maven使用依赖、继承和配置的概念,为父模块指定自己的子模块,并提供插件、配置信息等各子模块共有的资源。 通过将封装模块与实现具体功能的模块分开的方法,管理多个项目。
Gradle在这方面更明确,划分更明确,也更容易学习Gradle的多个项目管理。
在eclipse中安装Gradle插件后,创建新的Gradle项目,选择flat-Java-multi项目时,生成的项目就是多个项目管理的一个例子。
Gradle通过settings.Gradle文件指定项目的子项目,在构建初始化阶段(Initialization ),Gradle基于settings.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 高并发 疯狂创客圈 社群交流
基于Groovy的特定领域语言来声明项目设置
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
在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的仓库)
cmd输入命令:
gradle -v
关于 本文的代码,和技术问题, 请来尼恩 发起的Java 高并发 疯狂创客圈 社群交流
可能有很多人在纠结这个 Gradle 能不能像 maven 一样手动修改本地仓库,答案当然是肯定的,
而且很简单,只需要在环境变量里面做个配置即可如下图:
即添加一个名称为 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();
关于 本文的代码,和技术问题, 请来尼恩 发起的Java 高并发 疯狂创客圈 社群交流 ,
如你所见,Gradle 是一个通用工具。它可以通过脚本构建任何你想要实现的东西,真正实现开箱即用。
但前提是你需要在脚本中编写好代码才行。
大部分 Java 项目基本流程都是相似的:编译源文件,进行单元测试,创建 jar 包。
使用 Gradle 做这些工作不必为每个工程都编写代码。
Gradle 已经提供了完美的插件来解决这些问题。
插件就是 Gradle 的扩展,简而言之就是为你添加一些非常有用的默认配置。
Gradle 自带了很多插件,并且你也可以很容易的编写和分享自己的插件。
Java plugin 作为其中之一,为你提供了诸如编译,测试,打包等一些功能。
Java 插件为工程定义了许多默认值,如Java源文件位置。
如果你遵循这些默认规则,那么你无需在你的脚本文件中书写太多代码。
当然,Gradle 也允许你自定义项目中的一些规则,实际上,由于对 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 插件为你添加了众多任务。
但是它们只是在你需要构建项目的时候才能发挥作用。
最常用的就是 build 任务,这会构建整个项目。
当你执行 gradle build 时,Gralde 会编译并执行单元测试,并且将 src/main/*
下面 class 和资源文件打包。
运行 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'.
关于 本文的代码,和技术问题, 请来尼恩 发起的Java 高并发 疯狂创客圈 社群交流
其余一些较常用的任务有如下几个:
删除 build 目录以及所有构建完成的文件。
编译并打包 jar 文件,但不会执行单元测试。一些其他插件可能会增强这个任务的功能。例如,如果采用了 War 插件,这个任务便会为你的项目打出 War 包。
编译并测试代码。一些其他插件也可能会增强这个任务的功能。例如,如果采用了 Code-quality 插件,这个任务会额外执行 Checkstyle。
如何发布 jar 包?你需要告诉 Gradle 发布到到哪。
在 Gradle 中 jar 包通常被发布到某个仓库中。在下面的例子中,我们会将 jar 包发布到本地目录。当然你也可以发布到远程仓库或多个远程仓库中。
build.gradle
uploadArchives {
repositories {
flatDir {
dirs 'repos'
}
}
}
执行 gradle uploadArchives 以发布 jar 包。
Spring开发了一个类似Maven的依赖关系管理功能的Gradle插件( )。
plugins {
id "io.spring.dependency-management" version "1.0.8.RELEASE"
}
你有两个选项来配置依赖项管理:
依赖项的管理默认被, 应用到所有的配置或者一个或者多个指定的配置中。
使用:
分隔符的配置方式:
dependencyManagement {
dependencies {
dependency 'org.springframework:spring-core:4.0.3.RELEASE'
}
}
通过group
,name
,version
进行指定配置:
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'
}
}
}
可以使用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来使用其依赖的关系管理:
dependencyManagement {
imports {
mavenBom 'io.spring.platform:platform-bom:1.0.1.RELEASE'
}
}
dependencies {
compile 'org.springframework.integration:spring-integration-core'
}
这个配置会将Spring框架的Bom的依赖应用到当前的项目
//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')
//依赖明确的版本,标明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:+'
在项目的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 能不能像 maven 一样手动修改本地仓库,答案当然是肯定的,而且很简单,只需要在环境变量里面做个配置即可。
即添加一个名称为 GRADLE_USER_HOME ,值是你想放gradle仓库的地方即可。
GRADLE_USER_HOME
D:\gradle\.gradle\
2.如下图:
补充说明一点 据说 Gradle有个 Daemon配置,开启这个配置能有效的提高编译速度,具体是不是这样我没有做过对比因为我安装了gradle后就配置了。。。
3.具体操作如下:
在咱们的gradle仓库.gradle目录下创建一个gradle.properties 文件 ,再打开该文件在其中添加如下语句保存即可:
org.gradle.daemon=true
就这样简单。
gradle-demo
|---src
|---|---main
|---|---|---java(放置正式代码目录)
|---|---|---resources(放置正式配置文件目录)
|---|---|---webapp(放置页面元素)
|---|---test
|---|---|---java(放置单元测试代码目录)
|---|---|---resources(放置测试配置文件目录)
|---build.gradle
|---settings.gradle
关于 本文的代码,和技术问题, 请来尼恩 发起的Java 高并发 疯狂创客圈 社群交流 ,
idea中,使用groovy语言编辑器,
Tools - Groovy Console
方法调用,省略分号,小括号
定义字符串,单引号定义普通字符串,双引号可以引用变量,三引号按格式定义字符串
//介绍groovy编程语言
println("hello groovy"); // idea快捷键sout
println("hello groovy") // 可以省略最后的分号
println "hello groovy" // 可以省略括号
println 12 + ":" + 24
println 12 + 24
关于 本文的代码,和技术问题, 请来尼恩 发起的Java 高并发 疯狂创客圈 社群交流
// groovy 中定义变量
// def 是弱类型,groovy会自动根据情况来给变量赋予对应的类型
def foo = 18;
def bar = 疯狂创客圈
println foo;
使用 '...'
单引号来定义字符串,这种定义的字符串跟 Java 中定义的字符串是一样的,都是不可变的。
并且通过 str1.class 可以验证方式1输出的字符串类型就是 java.lang.String
使用 "..."
双引号来定义字符串,这种定义的字符串跟 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 的实现类。
因此可以知道单引号和双引号定义的字符串的区别在双引号定义的字符串可以通过 $
进行扩展,而单引号定义的字符串是不可变的,因此不可以扩展。
使用 '''...'''
双引号来定义字符串,这种定义的字符串跟 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对字符串的样式进行修改,例如换行,缩进。
关于 本文的代码,和技术问题, 请来尼恩 发起的Java 高并发 疯狂创客圈 社群交流
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 开头的
关于 本文的代码,和技术问题, 请来尼恩 发起的Java 高并发 疯狂创客圈 社群交流
def list = ['a','b']
// 往list中添加元素
list.add("疯狂创客圈")
list.add("java高并发社群")
list << 'c'
// 取出list中第3个元素
println list.get(2)
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}"
}
where = 疯狂创客圈
assert where == 疯狂创客圈
关于 本文的代码,和技术问题, 请来尼恩 发起的Java 高并发 疯狂创客圈 社群交流
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 属性配置)
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'
}
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如果不配置环境变量,则会在用户目录下(在Windows下是C盘的目录)创建.gradle目录,并将下载的依赖文件保存在这个目录里。
如果不想使用缺省目录,则可以设置环境变量GRADLE_USER_HOME的路径,就可改变gradle的缓存目录。
#配置M2_HOME
#mavenLocal() 时Gradle默认会按以下顺序去查找本地的maven仓库:
# USER_HOME/.m2/settings.xml > M2_HOME/conf/settings.xml > USER_HOME/.m2/repository。
mavenLocal()
repositories {
mavenLocal()
}
使用maven本地仓库,需要告知gradle本地maven仓库的地址,配置
M2_HOME
环境变量,
M2_HOME /conf/settings.xml
中指定我们本地仓库的位置
当我们使用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包引用地址
repositories {
mavenCentral()
}
GRADLE_USER_HOME\caches\modules-2\files-2.1\
文件夹下,配置 GRADLE_USER_HOME 环境变量,也可以直接在idea中指定位置
repositories {
mavenLocal()
// 阿里
maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' }
mavenCentral()
}
如果我们在
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包,观看其引用的地址)
/**
* 配置依赖的jar包
* gradle工程所有的jar包的坐标都在dependencies属性内放置
* 每一个jar包的坐标都有三个基本元素组成 group,name,version
* 添加坐标的时候都要带上jar包的作用域
*/
dependencies {
testCompile group: 'junit', name: 'junit', version: '4.12'
}
jar包的依赖范围(作用域):
compile:编译阶段:主程序、测试程序均有效;运行阶段:均有效
providedCompile:编译阶段:主程序、测试程序均有效;运行阶段:均无效(依靠运行时环境提供的jar包)
runtime:编译阶段:主程序、测试程序均无效(无需依赖);运行阶段:均有效
testCompile:编译阶段:主程序无效,测试程序有效;运行阶段:均有效
testRuntime:编译阶段:主程序、测试程序均无效(无需依赖);运行阶段:测试程序有效
jar包依赖具有传递性
依赖jar包版本存在冲突解决方案:
maven:最短路径原则,先声明原则
gradle:选择最高版本
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 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项目构建生命周期
初始化阶段:
通过settings.gradle判断哪些项目需要初始化,加载所有需要初始化的项目的build.gradle文件并为每个项目创建project对象
配置阶段:
执行各项目下的build.gradle脚本,完成project的配置,并且构造task任务关系图以便在执行阶段按照依赖关系执行task中的配置代码(直接定义在task下的代码,配置阶段就需要执行)
执行阶段:
通过配置阶段的task图,按顺序执行需要执行的任务中的动作代码(任务调用才会执行的代码,定义在doFirst或doLast中的代码)
关于 本文的代码,和技术问题, 请来尼恩 发起的Java 高并发 疯狂创客圈 社群交流 ,
我们可以直接覆写这些方法,在方法中加入我们自己的逻辑,(写在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"
}
父模块有settings.gradle文件,子模块没有settings.gradle文件,
settings中配置所拥有的子模块
rootProject.name = 'demo'
include 'service'
include 'app'
关于 本文的代码,和技术问题, 请来尼恩 发起的Java 高并发 疯狂创客圈 社群交流
父模块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 {
}
关于 本文的代码,和技术问题, 请来尼恩 发起的Java 高并发 疯狂创客圈 社群交流 ,
子模块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'
}
关于 本文的代码,和技术问题, 请来尼恩 发起的Java 高并发 疯狂创客圈 社群交流
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'
}
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版本支持的依赖配置有:
已经废弃的配置有:
此外依赖配置还可以加一些配置项,例如AndroidTestImplementation、debugApi等等。
常用的是implementation、api、compileOnly三个依赖配置,含义如下:
与compile对应,会添加依赖到编译路径,并且会将依赖打包到输出(aar或apk),
但是在编译时不会将依赖的实现暴露给其他module,也就是只有在运行时其他module才能访问这个依赖中的实现。
使用这个配置,可以显著提升构建时间,因为它可以减少重新编译的module的数量。
建议,尽量使用这个依赖配置。
与compile对应,功能完全一样,会添加依赖到编译路径,并且会将依赖打包到输出(aar或apk),
与implementation不同,这个依赖可以传递,
其他module无论在编译时和运行时都可以访问这个依赖的实现,也就是会泄漏一些不应该不使用的实现。
举个例子,
A依赖B,B依赖C,如果都是使用api配置的话,A可以直接使用C中的类(编译时和运行时),
而如果是使用implementation配置的话,在编译时,A是无法访问C中的类的。
与provided对应,
Gradle把依赖加到编译路径,编译时使用,不会打包到输出(aar或apk)。
这可以减少输出的体积,在只在编译时需要,在运行时可选的情况,很有用。
与apk对应,gradle添加依赖只打包到APK,运行时使用,但不会添加到编译路径。
这个很少使用。
与compile对应,用于注解处理器的依赖配置,
这个很少使用。
可以查看单个module或者这个project的依赖,通过运行依赖的Gradle任务,如下:
View -> Tools Windows -> Gradle(或者点击右侧的Gradle栏);
展开 AppName -> Tasks -> android,然后双击运行AndroidDependencies。
运行完,就会在Run窗口打出依赖树了。
随着很多依赖加入到项目中,难免会出现依赖冲突,出现依赖冲突如何解决?
定位冲突
依赖冲突可能会报类似下面的错误:
Program type already present com.example.MyClass
通过查找类的方式(command + O)定位到冲突的依赖,进行排除。
如何排除依赖
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缺省方式使用groovy作为DSL的支持方式,kotlin也作为支持方式之一,
gradle init也可以直接指定kotlin的方式生成相关的文件,使用方式如下
使用命令:gradle init --dsl kotlin
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的语法,很容易就可以将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 (科特林)是一个用于现代多平台应用的静态编程语言 ,由 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 在它的核心中有意地提供了一些小但有用的功能,用于在真实世界中的自动化。
所有有用的功能,例如以能够编译 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 插件,除其他外,还做了下列操作:
所有这一切都发生在 apply plugin: “java” 这一步过程中。在上面例子中,我们在约定配置被执行之后,修改了类文件所需的位置。在上面的示例中可以注意到,compileJava.destinationDir 的值也被修改了,以反映出配置的修改。
考虑一下另一种消费类文件的任务的情况。如果这个任务使用 sourceSets.main.output.classesDir 的值来配置,那么修改了这个位置的值,无论它是什么时候被修改,将同时更新 compileJava 任务和这一个消费者任务。
这种配置对象的属性以在所有时间内(甚至当它更改的时候)反映另一个对象的任务的值的能力被称为“映射约定”。它可以令 Gradle 通过约定优于配置及合理的默认值来实现简洁的配置方式。而且,如果默认约定需要进行修改时,也不需要进行完全的重新配置。如果没有这一点,在上面的例子中,我们将不得不重新配置需要使用类文件的每个对象。
关于 本文的代码,和技术问题, 请来尼恩 发起的Java 高并发 疯狂创客圈 社群交流
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 | 将一些共享的公约属性添加到项目中,它们与报告的生成有关。 |
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 插件-源集任务
任务名称 | 依赖于 | 类型 | 描述 |
---|---|---|---|
SourceSet Java |
所有产生源集编译类路径的任务。 | JavaCompile | 使用 javac 编译给定的源集中的 Java 源文件。 |
SourceSet Resources |
- | Copy | 把给定的源集的资源文件拷贝到类目录中。 |
sourceSet Classes |
SourceSet Resources。某些插件还为源集添加了额外的编译任务。 |
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 可执行文件的参考文档中描述的标准 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 |
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 源集中的所有单元测试。测试执行完成后,它还会生成一份报告。同时支持 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 现在就可以处理,而有一些则将在未来得到实现:
测试过滤功能具有以下的特征:
在构建脚本中过滤测试
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 的文档。
使用命令行选项的一些示例:
这种机制已经被上述的“测试过滤”所取代。 设置一个 taskName.single = testNamePattern 的系统属性将会只执行匹配 testNamePattern 的那些测试。这个 taskName 可以是一个完整的多项目路径,比如“sub1:sub2:test”,或者仅是一个任务名称。testNamePattern 将用于形成一个“**/testNamePattern*.class” 的包含模式。如果这个模式无法找到任何测试,那么将会抛出一个异常。这是为了使你不会误以为测试通过。如果执行了一个以上的子项目的测试,该模式会被应用于每一个子项目中。如果在一个特定的子项目中,找不到测试用例,那么将会抛出异常。在这种情况下你可以使用路径标记法的模式,这样该模式就会只应用于特定的子项目的测试任务中。或者你可以指定要执行的任务的完整限定名称。你还可以指定多个模式。示例:
Test 任务通过检查编译过的测试类来检测哪些类是测试类。默认情况下它会扫描所有的.class 文件。您可以设置自定义的 includes 或 excludes,这样就只有这些类才会被扫描。根据所使用的测试框架 (JUnit 或 TestNG),会使用不同的标准进行测试类的检测。
当使用 JUnit 时,我们扫描 JUnit 3 和 4 的测试类。如果满足以下的任何一个条件,这个类就被认为是一个 JUnit 测试类:
当使用 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 任务默认情况下会生成以下结果。
您可以使用 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 支持参数化测试方法,允许一个特定的测试方法使用不同的输入执行多次。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 文件在 archives 依赖配置中被声明为一个构件。这意味着这个 JAR 文件被包含在一个依赖它的项目的类路径中。如果你把你的项目上传到仓库,这个 JAR 文件会被声明为依赖描述符的一部分。你可以在第16.8节,“创建档案”和第51章, 发布 artifact 中了解如何使用 archives 和配置 artifact。
每个 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")
疯狂创客圈 JAVA 高并发 总目录
https://www.cnblogs.com/crazymakercircle/p/9904544.html
ThreadLocal(史上最全)
https://www.cnblogs.com/crazymakercircle/p/14491965.html
3000页《尼恩 Java 面试宝典 》的 35个面试专题 :
https://www.cnblogs.com/crazymakercircle/p/13917138.html
价值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