Gradle 的文件操作
本地文件:
使用 Project.file(java.lang.Object)方法,通过指定文件的相对路径或绝对路径来对文件的操作,其中相对路径为相对当前 project[根 project 或者子 project]的目录。其实使用 Project.file(java.lang.Object)方法创建的 File 对象就是 Java 中的 File 对象,我们可以使用它就像在 Java 中使用一样。示例代码如下:
 //使用相对路径File configFile = file('src/conf.xml')configFile.createNewFile();// 使用绝对路径configFile = file('D:\\conf.xml')println(configFile.createNewFile())// 使用一个文件对象configFile = new File('src/config.xml')println(configFile.exists())
   复制代码
 文件集合
文件集合就是一组文件的列表,在 Gradle 中,文件集合用 FileCollection 接口表示我们可以使用 Project.files(java.lang.Object[])方法来获得一个文件集合对象,如下代码创建一个 FileCollection  实例:
 def collection = files('src/test1.txt',new File('src/test2.txt'),['src/test3.txt', 'src/test4.txt']) collection.forEach(){File it ->it.createNewFile() //创建该文件println it.name //输出文件名}Set set1 = collection.files // 把文件集合转换为java中的Set类型Set set2 = collection as SetList list = collection as List//  把文件集合转换为java中的List类型for (item in list) { println item.name}def union = collection + files('src/test5.txt') // 添加或者删除一个集合def minus = collection - files('src/test3.txt') union.forEach(){File it ->println it.name}
   复制代码
 
对于文件集合我们可以遍历它;也可以把它转换成 java 类型;同时还能使用+来添加一个集合,或使用-来删除集合。
文件树
文件树是有层级结构的文件集合,一个文件树它可以代表一个目录结构或一 ZIP 压缩包中的内容结构。文件树是从文件集合继承过来的,所以文件树具有文件集合所有的功能。我们可以使用 Project.fileTree(java.util.Map)方法来创建文件树对象, 还可以使用过虑条件来包含或排除相关文件。
 tree = fileTree('src/main').include('**/*.java')// 第一种方式:使用路径创建文件树对象,同时指定包含的文件//第二种方式:通过闭包创建文件树:tree = fileTree('src/main') {include '**/*.java'}tree = fileTree(dir: 'src/main', include: '**/*.java') //第三种方式:通过路径和闭包创建文件树:具名参数给map传值tree = fileTree(dir: 'src/main', includes: ['**/*.java', '**/*.xml', '**/*.txt'], exclude: '**/*test*/**')tree.each {File file -> // 遍历文件树的所有文件println file println file.name}
   复制代码
 文件拷贝
我们可以使用 Copy 任务来拷贝文件,通过它可以过虑指定拷贝内容,还能对文件进行重命名操作等。Copy 任务必须指定一组需要拷贝的文件和拷贝到的目录,这里使用 CopySpec.from(java.lang.Object[])方法指定原文件;使用 CopySpec.into(java.lang.Object)方法指定目标目录。
 task copyTask(type: Copy) { from 'src/main/resources' into 'build/config'}
   复制代码
 
from()方法接受的参数和文件集合时 files()一样。当参数为一个目录时,该目录下所有的文件都会被拷贝到指定目录下(目录自身不会被拷贝);当参数为一个文件时,该文件会被拷贝到指定目录;如果参数指定的文件不存在,就会被忽略; 当参数为一个 Zip 压缩文件,该压缩文件的内容会被拷贝到指定目录。into()方法接受的参数与本地文件时 file()一样。 示例代码如下
 task copyTask(type: Copy) {    // 拷贝src/main/webapp目录下所有的文件    from 'src/main/webapp'    // 拷贝单独的一个文件    from 'src/staging/index.html'    // 从Zip压缩文件中拷贝内容    from zipTree('src/main/assets.zip')    // 拷贝到的目标目录    into 'build/explodedWar'}
   复制代码
 
在拷贝文件的时候还可以添加过虑条件来指定包含或排除的文件,示例如下:
 task copyTaskWithPatterns(type: Copy) { from 'src/main/webapp'into 'build/explodedWar' include '**/*.html' include '**/*.jsp'exclude { details -> details.file.name.endsWith('.html') }}
   复制代码
 
在拷贝文件的时候还可以对文件进行重命名操作,示例如下:
 task rename(type: Copy) { from 'src/main/webapp' into 'build/explodedWar'    // 使用一个闭包方式重命名文件    rename { String fileName -> fileName.replace('-staging-', '')    }}
   复制代码
 
在上面的例子中我们都是使用 Copy 任务来完成拷贝功能的,那么有没有另外一种方式呢?答案是肯定的,那就是
 Project.copy(org.gradle.api.Action)方法。下面示例展示了 copy()方法的使用方式:task copyMethod { doLast {        copy {        from 'src/main/webapp' into 'build/explodedWar' include '**/*.html' include '**/*.jsp'        }    }}
   复制代码
 归档文件
通常一个项目会有很多的 Jar 包,我们希望把项目打包成一个 WAR,ZIP 或 TAR 包进行发布,这时我们就可以使用 Zip,Tar,Jar,War 和 Ear 任务来实现,不过它们的用法都一样,所以在这里我只介绍 Zip 任务的示例。首先,创建一个 Zip 压缩文件,并指定压缩文件名称,如下代码所示:
 apply plugin: 'java' version=1.0task myZip(type: Zip) { from 'src/main‘    into ‘build’ //保存到build目录中    baseName = 'myGame'}println myZip.archiveName
   复制代码
 
执行命令gradle -q myZip,输出结果为:
gradle -q myZip myGame-1.0.zip 最后,我们可以使用 Project.zipTree(java.lang.Object)和 Project.tarTree(java.lang.Object)方法来创建访问 Zip 压缩包的文件树对象,示例代码如下:
 // 使用zipTreeFileTree zip = zipTree('someFile.zip')// 使用tarTreeFileTree tar = tarTree('someFile.tar')
   复制代码
 官方文档
Dependencies-依赖
依赖的方式:
 // 简写implementation 'org.apache.logging.log4j:log4j:2.17.2'// 完整写法implementation group: 'org.apache.logging.log4j', name: 'log4j', version: '2.17.2'
   复制代码
 
 // 依赖当前项目下的某个模块[子工程]implementation project(':subject01')
   复制代码
 
 // 直接依赖本地的某个jar文件implementation files('libs/foo.jar', 'libs/bar.jar')// 配置某文件夹作为依赖项implementation fileTree(dir: 'libs', include: ['*.jar'])
   复制代码
 依赖的下载
当执行 build 命令时,gradle 就会去配置的依赖仓库中下载对应的 Jar,并应用到项目中。
依赖的类型
相关插件的写法:
 plugins{    // java 插件    id 'java'    // application 插件    id 'application'    // java-library 插件    id 'java-library'}
   复制代码
 官方文档
https://docs.gradle.org/current/userguide/java_library_plugin.html#java_library_plugin
https://docs.gradle.org/current/userguide/upgrading_version_6.html
https://docs.gradle.org/current/userguide/java_library_plugin.html#java_library_plugin
https://docs.gradle.org/current/userguide/java_plugin.html
api 和 implementation 的区别
api 举例:api 的适用场景是多 module 依赖,moduleA 工程依赖了 module B,同时 module B 又需要依赖了 module C,modelA 工程也需要去依赖 module C,这个时候避免重复依赖 module,可以使用 module B api  依赖的方式去依赖 module C,modelA 工程只需要依赖 moduleB 即可。
总之,除非涉及到多模块依赖,为了避免重复依赖,咱们会使用 api,其它情况我们优先选择 implementation,拥有大量的 api 依赖项会显著增加构建时间。
场景:有 ABCD 四个模块:
- A implemeteation B, B implemetation C, 则 A 不能使用 C 
- A implemetation B,B api C,则 A 可以使用 C 
- A implemetation B,B implemetation C,C api D,则 B 可以使用 D,但 A 不能使用 D 
- A implemetation B,B api C,C api D,这样 A 可以使用 D 
上面的情况都是在编译时
依赖冲突和解决方案
冲突
依赖冲突是指 "在编译过程中, 如果存在某个依赖的多个版本, 构建系统应该选择哪个进行构建的问题",如下所示
A、B、C 都是本地子项目 module,log4j 是远程依赖。编译时: B 用 1.4.2 版本的  log4j,C 用 2.2.4 版本的  log4j,B 和 C 之间没有冲突打包时: 只能有一个版本的代码最终打包进最终的 A 对应的 jar |war 包,对于  Gradle 来说这里就有冲突了
解决方案
方案一:default
方案二: exclude
 exclude排除一个依赖
 dependencies {    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'    // hibernate 自带了一个1.6的slf4j    implementation('org.hibernate:hibernate-core:3.6.3.Final'){        //排除某一个库(slf4j)依赖:如下三种写法都行        // 1.通过组名        exclude group: 'org.slf4j'         // 2.通过依赖的名字        // exclude module: 'slf4j-api'        // 3.        // exclude group: 'org.slf4j',module: 'slf4j-api'    }    implementation 'org.slf4j:slf4j-api:1.4.0'}
   复制代码
 方案三:transitive(false)
禁止依赖的传递transitive(false)不建议使用这种方式
 dependencies {    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'    implementation('org.hibernate:hibernate-core:3.6.3.Final'){        //不允许依赖传递,一般不用        transitive(false)    }  //排除之后,使用手动的引入即可    implementation 'org.slf4j:slf4j-api:1.4.0'}
   复制代码
 方案四:!!
强制使用某个版本
 dependencies {    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'    implementation('org.hibernate:hibernate-core:3.6.3.Final')    // 1.强制使用某个版本!!【官方建议使用这种方式】    // implementation('org.slf4j:slf4j-api:1.4.0!!')        // 2.这种效果和上面那种一样,强制指定某个版本    implementation('org.slf4j:slf4j-api:1.4.0'){        version{          strictly("1.4.0")        }  }}
   复制代码
 如何配置项目若有依赖冲突则报错
 // 当我们有下面配置,当 Gradle 构建遇到依赖冲突时,就立即构建失败configurations.all() {  Configuration configuration ->      //当遇到版本冲突时直接构建失败        configuration.resolutionStrategy.failOnVersionConflict()}
   复制代码
 
报错信息如下:
扩展
 // :+ 获取最新版本的jar包,在你配置的maven仓库中去查找最新的 implementation 'org.slf4j:slf4j-api:+'// 或者使用 :lastest.integration  这种属于动态版本声明,不建议这么使用implementation 'org.slf4j:slf4j-api:lastest.integration'
   复制代码
 Gradle 插件
- 促进代码重用、减少功能类似代码编写、提升工作效率 
- 促进项目更高程度的模块化、自动化、便捷化 
- 可插拔式的的扩展项目的功能 
插件的作用
- 可以添加任务【task】到项目中,从而帮助完成测试、编译、打包等。 
- 可以添加依赖配置到项目中。 
- 可以向项目中拓展新的扩展属性、方法等。 
- 可以对项目进行一些约定,如应用 Java 插件后,约定 src/main/java 目录是我们的源代码存在位置,编译时编译这个目录下的 Java 源代码文件。 
插件的分类
脚本插件
使用方式:
 ext {    company= "派大星"     cfgs = [      compileSdkVersion : JavaVersion.VERSION_1_8    ]    spring = [        version : '5.0.0'    ]}
   复制代码
 
 apply from :"version.gradle"
// 然后便可以使用task taskVersion{    doLast{      println "公司名称为:${company},JDK版本是${cfgs.compileSdkVersion},版本号是${spring.version}"    }}
   复制代码
 
脚本的意义:脚本文件模块化的基础,可按功能把我们的脚本进行拆分一个个公用、职责分明的文件,然后在主脚本文件引用, 比如:将很多共有的库版本号一起管理、应用构建版本一起管理等。
二进制插件-对象插件之内部插件[核心插件]
官方文档
https://docs.gradle.org/current/userguide/plugin_reference.html二进制插件[对象插件]就是实现了 org.gradle.api.Plugin  接口的插件,每个 Java Gradle 插件都有一个 plugin id。
使用方式:
- plugins DSL 方式 
- apply 方式 
- map 具名参数的方式 
 /*** apply:map具名参数的方式*  key: plygin   value:  1.插件id、      2.插件的全类名、      3. 插件的简类名(如果插件的类所在的包已经被build.gradle引入锅可以使用这种方式)*/// 方式1.插件id、apply plugin : 'java'// 方式2. 插件的全类名apply plugin : org.gradle.api.plugins.JavaPlugin// 方式3. 由于包 org.gradle.api.plugins 默认引入所以可以使用apply plugin:JavaPlugin
   复制代码
 
- apply 闭包的方式 
第三方插件
如果是使用第三方发布的二进制插件,一般需要配置对应的仓库和类路径
使用方式:
- 传统方式 
 buildscript {    ext {      springBootVersion = "2.3.3.RELEASE"    }    repositories {        mavenLocal()        maven { url 'http://maven.aliyun.com/nexus/content/groups/public' }        jcenter()        }    // 此处先引入插件    dependencies {      classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")    }}// 在应用插件apply plugin: 'org.springframework.boot' //社区插件,需要事先引入,不必写版本号
   复制代码
 
- plugins DSL 方式 
前提是:插件被托管在了https://plugins.gradle.org/ 网站上,就可以不用在 buildScript 里配置 classpath 依赖了,直接使用新出的 plugins DSL 方式引入。代码如下:
 plugins {  id 'org.springframework.boot' version '2.4.1'}
   复制代码
 
** 注意:**
- 如果使用老式插件方式- buildscript{}要放在 build.gradle 文件的最前面,而新式- plugins{}没有该限制。
 
- 托管在网站 gradle 插件官网的第三方插件有两种使用方式,一是传统的 buildscript 方式,一种是 plugins DSL 方式 。 
用户自定义插件
参考官方地址:https://docs.gradle.org/current/userguide/custom_plugins.html
 class GreetingPlugin implements Plugin<Project> {    void apply(Project project) {        project.task('hello') {            doLast {                println 'Hello from the GreetingPlugin'            }        }    }}
// Apply the pluginapply plugin: GreetingPlugin
   复制代码
 
buildSrc 项目
插件被当前工程的多个 project 使用的配置方式
buildSrc 是 Gradle 默认的插件目录,编译 Gradle 的时候会自动识别这个目录,将其中的代码编译为插件。
- 首先在- 项目根目录下先建立一个名为 buildSrc 的 java Module,并且在项目根目录下的- setting.gradle文件中将 buildSrc 的- included buildSrc移除,重新构建、刷新。然后只保留- buidlSrc目录下的- build.gradle和- src/main 、- .gradle目录,其他全部删掉,新建- groovy目录。注意名字一定是 buildSrc,不然会找不到插件。
 
- 然后修改- build.gradle中的内容,如下:
 
 apply plugin: 'groovy' // 必须apply plugin: 'maven-publish'
dependencies {  implementation gradleApi() //必须    implementation localGroovy() //必须}
repositories {  google()     jcenter()  mavenCentral() //必须}
//把项目入口设置为src/main/groovy sourceSets {    main {        groovy {          srcDir 'src/main/groovy'        }    }}
   复制代码
 
- 创建入口目录,在 src/main 下创建代码入口目录:如图: 
- 编写插件代码,后缀为- .groovy(com.vipbbo.Text.groovy)
 
 package com.atguiguimport org.gradle.api.Plugin import org.gradle.api.Projectclass Text implements Plugin<Project>{     @Override  void apply(Project project) {         project.task("atguigu"){          doLast{              println("自定义atguigu插件")            }        }    }}
   复制代码
 
- 接下来在 src/main 目录下创建- resources目录,在 resources 目录下创建- META-INF,在 META-INF 下创建- gradle-plugins目录,在 gradle-plugins 下创建以- .properties结尾的文件
 
- properties 文件可以自己命名,但是要以- .properties文件结尾,比如 com.vipbbo.plugin.properties,其中- com.vipbbo.plugin就是定义的包名路径
 
- 最后需要在 properties 文件中指明我们实现插件的全类名,- implementation-class =  com.vipbbo.Text
 
 
接下来就可以使用了:在 module 引入我们刚刚写的插件,
 apply plugin: 'com.vipbbo.plugin'
   复制代码
 
然后执行插件的 Task:
既能被当前功能使用,也能被其它工程使用的插件(自定义插件发布到 maven 仓库)
- 第一步:首先将上述步骤插件被当前工程的多个project使用的配置方式 的 步骤重复一遍,或者将- buildSrc目录复制一份(需要重命名-paidaxing  )。修改文件夹名称,然后在项目根目录下的- setting.gradle文件中使用- include引入。
 
- 第二步:修改 paidaxing 目录下的(或者试你新建的 buildSrc 目录下的)	- build.gralde文件,发布到 maven 仓库中。修改内容如下:
 
 apply plugin: 'groovy' // 必 须apply plugin: 'maven-publish' dependencies {  implementation gradleApi() //必须    implementation localGroovy() //必须}repositories {  google()     jcenter()  mavenCentral() //必须}sourceSets { //把项目入口设置为src/main/groovy     main {        groovy {           srcDir 'src/main/groovy'        }    }}publishing {    publications {        myLibrary(MavenPublication) {          groupId = 'com.atguigu.plugin' //指定GAV坐标信息            artifactId = 'library'          version = '1.1'          from components.java//发布jar包          //from components.web///引入war插件,发布war包        }    }    repositories {    maven { url "$rootDir/lib/release" }    //发布项目到私服中      // maven {     //      name = 'myRepo' //name属性可选,表示仓库名称,url必填     //      //发布地址:可以是本地仓库或者maven私服     //      //url = layout.buildDirectory.dir("repo")     //     // url='http://my.org/repo'     //      // change URLs to point to your repos, e.g. http://my.org/repo     //      //认证信息:用户名和密码     //      credentials {     //          username = 'joe'     //          password = 'secret'     //      }      // }    }}
   复制代码
 
- 第三步:执行- publish 指令,发布到根 project 或者 maven 私服仓库。
 
打包到本地的效果
- 第四步:使用插件,在项目下的- build.gradle文件中将插件添加到 classpath 即可
 
 // 这个标签一定要放在前面buildscript {    repositories {      maven { url "$rootDir/lib/release" }    }    dependencies {      classpath "com.atguigu.plugin:library:1.1"    }}
apply plugin: 'java'//是在 atguiguplugin  中定义的插件 IDapply plugin: 'com.atguigu.plugin'
   复制代码
 
执行 gradle build命令即可验证,看是否打印
build.gradle 常见属性
常见属性代码
 //指定使用什么版本的JDK语法编译源代码,跟编译环境有关,在有java插件时才能用sourceCompatibility = JavaVersion.VERSION_1_8//指定生成特定于某个JDK版本的class文件:跟运行环境有关,在有java插件时才能用targetCompatibility = 1.8//业务编码字符集,注意这是指定源码解码的字符集[编译器] compileJava.options.encoding "UTF-8"//测试编码字符集,注意这是指定源码解码的字符集[编译器] compileTestJava.options.encoding "UTF-8"//编译JAVA文件时采用UTF-8:注意这是指定源码编码的字符集【源文件】tasks.withType(JavaCompile) {  options.encoding = "UTF-8"}//编译JAVA文件时采用UTF-8:注意这是指定文档编码的字符集【源文件】tasks.withType(Javadoc) {  options.encoding = "UTF-8"}
   复制代码
 
提示 1:group+name+version 类似于 maven 的 group+artifactId+version 提示 2:encoding 解决业务代码与测试代码中文乱码问题
Repositories
 repositories {    //gradle中会按着仓库配置的顺序,从上往下依次去对应的仓库中找所需要的jar包:    //如果找到,则停止向下搜索,如果找不到,继续在下面的仓库中查找    //指定去本地某个磁盘目录中查找:使用本地file文件协议:一般不用这种方式    maven { url 'file:///D:/repos/mavenrepos3.5.4'} maven { url "$rootDir/lib/release" }    //指定去maven的本地仓库查找    mavenLocal()    //指定去maven的私服或者第三方镜像仓库查找    maven { name "Alibaba" ; url "https://maven.aliyun.com/repository/public" }     maven { name "Bstek" ; url "https://nexus.bsdn.org/content/groups/public/" }    //指定去maven的远程仓库查找:即 https://repo.maven.apache.org/maven2/    mavenCentral()    //去google仓库查找google()}
   复制代码
 subproject 和 allproject
 allprojects {    tasks.create('hello') { doLast {    task ->      println "project name is $task.project.name"    }    }}subprojects {    hello.doLast{         task->       println "here is subprojects $task.project.name"    }}
   复制代码
 
 // 这个子工程的名字 subject01必须出现在setting.gradle文件中project('subject01') {    group=""    verson=""    apply plugin : java    // 给子工程配置依赖    dependencies {        implementation('org.hibernate:hibernate-core:3.6.3.Final')    }    task subject01 {        doLast {          println 'for subject01'        }    }}
   复制代码
 
注意:allprojects、subproject不支持plugins DSL语句引入,如下:
 plugins {    id 'java'}// =============================// 这个子工程的名字 subject01必须出现在setting.gradle文件中project('subject01') {    // 这个不支持    plugins {      id 'java'  }    dependencies {        implementation('org.hibernate:hibernate-core:3.6.3.Final')    }    task subject01 {        doLast {          println 'for subject01'        }    }}
   复制代码
 ext 用户自定义属性
 //自定义一个Project的属性ext.age = 18//通过代码块同时自定义多个属性ext {    phone = 19292883833    address="北京尚硅谷"}task extCustomProperty {    //在task中自定义属性    ext {      desc = "奥利给"    }    doLast {      println " 年 龄 是 :${age}" println "电话是:${phone}" println "地址是:${address}" println "尚硅谷:${desc}"    }}
   复制代码
 
执行命令
注意:
**ext 配置的是用户自定义属性,而gradle.properties 中一般定义 系统属性、环境变量、项目属性、JVM 相关配置信息。**例如:gradle.properties 文件案例:加快构建速度的,gradle.properties 文件中的属性会自动在项目运行时加载
 ## 设置此参数主要是编译下载包会占用大量的内存,可能会内存溢出org.gradle.jvmargs=-Xms4096m -Xmx8192m ## 开启gradle缓存org.gradle.caching=true #开启并行编译org.gradle.parallel=true #启用新的孵化模式org.gradle.configureondemand=true #开启守护进程org.gradle.daemon=true
   复制代码
 官方文档:
https://docs.gradle.org/current/userguide/build_environment.html#sec%3Agradle_configuration_properties
buildScript
- 主要是给插件使用 
- 构建脚本- buildScript必须要在 build.gradle 文件的最前方
 
- 对于多项目构建,项目的 buildscript ()方法声明的依赖关系可用于其所有子项目的构建脚本 
- 构建脚本依赖可能是 Gradle 插件。案例如下所示: 
 //老式apply插件的引用方式,使用apply+buildscriptbuildscript {     ext {    springBootVersion = "2.3.3.RELEASE"  }    repositories {        mavenLocal()        maven { url 'http://maven.aliyun.com/nexus/content/groups/public' }         jcenter()    }//此处引入插件    dependencies {      classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")    }}apply plugin: 'java' //核心插件,无需事先引入apply plugin: 'org.springframework.boot' //社区插件,需要事先引入,才能应用,不必写版本号
   复制代码
 整理不易。欢迎点赞关注
微信搜索【码上遇见你】获取 Java 学习资料。获取更多精彩内容。
评论