原文地址:https://blog.gaoyuexiang.cn/2...,内容没有差异。html
在上一篇文章中,咱们在没有使用任何插件的状况下,练习了使用 Gradle
构建 Java
项目,最后获得一个脆弱的构建脚本和不符合约定的目录结构。java
对此,Gradle
使用了插件来解决这些问题。spring
Gradle
中的插件,能够给咱们带来不少好处,包括:shell
Task
Gradle
的核心类Gradle
将插件分为两类,Script Plugin
& Binary Plugin
。segmentfault
那些写到单独的 gradle
文件中,并被 build.gradle
文件使用的脚本文件,就是 Script Plugin
。常见的实践是将某一插件或某一方面的配置写到单独的文件中,好比 jacoco.gradle
,而后经过下面的语法导入到 build.gradle
文件中:app
apply from: file("$projectDir/gradle/jacoco.gradle")
而常见的 java
、idea
这样的 core Plugin
和 org.springframework.boot
等能够在 https://plugins.gradle.org/ 找到的插件,就是 Binary Plugin
,它们经过 plugins{}
语法块引入:maven
plugins { id 'java' id 'org.springframework.boot' version '2.2.4.RELEASE' }
接下来,咱们接着上一篇文章的例子,使用 Java Plugin
来改造咱们的构建脚本。ide
Java
插件的文档:https://docs.gradle.org/curre...工具
Java
Plugin如上所述,咱们使用 Java Plugin
须要先导入它:性能
plugins { id 'java' }
由于 Java
插件是 Gradle
提供的核心插件,它是和 Gradle
版本绑定的,因此不须要使用 version
参数。
SourceSet
引入 Java
插件后,咱们先来了解一个核心概念:SourceSet
。这是 Java
插件引入的概念,每个 SourceSet
都包含了一组相关的资源。默认状况下,一个 SourceSet
对应 src
目录下的一个目录,目录名称就是 SourceSet
的名称;目录下会有一个 java
目录和一个 resources
目录。根据约定,这两个目录分别是存放 java
文件的目录和存放配置等资源文件的目录。
SourceSet
还有更多的信息能够配置,参见:https://docs.gradle.org/curre...:java_source_sets
Java
插件还默认配置好了两个 SourceSet
,分别是 main
& test
。因此在使用 Java
插件后,无需任何配置,就能够获得约定的目录结构:
❯ tree src src ├── main │ ├── java │ └── resources └── test ├── java └── resources
因此,咱们须要将 HelloWorld.java
从 src
目录移动到符合约定的 src/main/java
目录下:
❯ tree src src └── main └── java └── HelloWorld.java
Task
Java
插件引入的 Task
接着咱们来看看 Task
须要作哪些修改。
Java
插件引入了下面的这些 Task
,而且添加了依赖关系:
其中有四个 task
是由 base plugin
添加的:clean
, check
, assemble
和 build
。
其中,check
, assemble
和 build
是 lifecycle task
,自己不执行任务,只是定义了执行它们时应该执行什么样的任务:
check
:聚合全部进行验证操做的 task
,好比测试assemble
:聚合全部会产生项目产出物的 task
,好比打包build
:聚合前面两个 task
其余的 task
中,很容易发现,compileJava
与 compileTestJava
、processResources
与 processTestResources
、classes
与 testClasses
命名相似。实际上,每一对 task
表达的是一样的含义,只是一个针对 main sourceSet
,一个针对 test sourceSet
而已。若是你建立了一个自定义的 SourceSet
,那 Java
插件会自动的添加 compileSourceSetJava
、processSourceSetResources
和 sourceSetClasses
,其中的 sourceSet
就是 SourceSet.name
。
compileJava
:编译该 sourceSet
下的 java
文件processResource
:将该 sourceSet
中的资源文件复制到 build
目录中classes
:准备打包和执行须要的 class
文件和资源文件注意,执行测试是test
任务,它没有由于添加sourceSet
而自动添加sourceSetTest
方法。由于自定义的SourceSet
不必定是组件测试之类的不一样类别的测试。因此,若是你添加了这样的SourceSet
,须要本身手动编写Test
类型的测试task
。
由上面的了解可知,Java
插件已经为咱们添加了 compileJava
和 jar
这两个 task
,因此咱们不须要再建立这样的 task
。可是咱们仍是能够对这些 task
进行配置。
好比,咱们仍然但愿控制 jar
产出的文件名,那咱们的脚本就能够改为这样:
// task compileJava(type: JavaCompile) { // source fileTree("$projectDir/src") // include "**/*.java" // destinationDir = file("${buildDir}/classes") // sourceCompatibility = '1.8' // targetCompatibility = '1.8' // classpath = files("${buildDir}/classes", configurations.forHelloWorld) // } // tasks.create('jar', Jar) jar { archiveBaseName = 'base-name' archiveAppendix = 'appendix' archiveVersion = '0.0.1' // from compileJava.outputs // include "**/*.class" manifest { attributes("something": "value") } // setDestinationDir file("$buildDir/lib") }
其中注释的部分能够删除,这里仅仅做为修改先后的对比。
根据 assemble
的定义,咱们的 fatJar
的输出应当看做项目的产出物,因此须要让 assemble
依赖于 fatJar
:
assemble.dependsOn fatJar
Dependency Configuration
Java
插件引入的 Configuration
上一篇文章讲到,在 Gradle
中声明依赖,须要关联到 configuration
。Java
插件也提早为咱们设计了一些 configuration
,他们的主要关系能够经过两幅图来表示。
与 main sourceSet
相关的:
其中:
configuration
configuration
task
使用的 configuration
task
由这个图,咱们就能看出声明到不一样 configuration
中的依赖最终会在什么地方使用到。
与 test sourceSet
相关的:
其中的字体和颜色与上一张图一致。
咱们能够看到,除去 compile
, implementation
, runtime
和 rumtimeOnly
,其余的 configuration
与上图几乎一致。这里画出他们,仅仅是为了展现出扩展关系而已。
若是你使用过之前版本的Gradle
,想必会比较好奇为何Compile
会被废弃。这实际上是出于构建工具的性能的考虑,关闭掉没必要要的传递依赖。
你也许也发现了,和 task
同样,有一些名称相近的 configuration
,因此很天然的推测:添加了自定义的 SourceSet
后,Java
插件会自动的添加一些 configuration
。这些 sourceSet configuration
均可以在 Java
插件的页面上找到。
首先,咱们能够直接使用 Java
插件提供的 implementation
,而不须要本身建立任何 configuration
:
// configurations { // forHelloWorld // } dependencies { // forHelloWorld group: 'com.google.guava', name: 'guava', version: '28.2-jre' implementation group: 'com.google.guava', name: 'guava', version: '28.2-jre' }
一样,注释只是为了对比。
接着,咱们的 fatJar
也不能再使用 forHelloWorld
这个 configuration
,但也不能直接使用 implementation
,而应该使用 runtimeClasspath
这个给 task
消费的、语义更符合咱们使用目标的 configuration
:
task('fatJar', type: Jar) { archiveBaseName = 'base-name' archiveAppendix = 'appendix' archiveVersion = '0.0.1' archiveClassifier = 'boot' from compileJava // from configurations.forHelloWorld.collect { from configurations.rumtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } manifest { attributes "Main-Class": "HelloWorld" } setDestinationDir file("$buildDir/libs") }
通过使用 Java
插件,并对构建脚本的修改,咱们获得了更具备鲁棒性、实现了约定优于配置的构建脚本。
完整的脚本以下:
plugins { id 'java' } repositories { mavenCentral() } dependencies { implementation group: 'com.google.guava', name: 'guava', version: '28.2-jre' } compileJava.doLast { println 'compile success!' } jar { archiveBaseName = 'base-name' archiveAppendix = 'appendix' archiveVersion = '0.0.1' manifest { attributes("something": "value") } } task('fatJar', type: Jar) { archiveBaseName = 'base-name' archiveAppendix = 'appendix' archiveVersion = '0.0.1' archiveClassifier = 'boot' from compileJava from configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } manifest { attributes "Main-Class": "HelloWorld" } setDestinationDir file("$buildDir/libs") } assemble.dependsOn(fatJar)