gradle 文件操作和Dependencies

353 阅读7分钟

1、文件操作

1、本地文件

使用 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())

2、文件集合

文 件 集 合 就 是 一 组 文 件 的 列 表 , 在 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 
Set List 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 
}

3、文件树

文件树是有层级结构的文件集合,一个文件树它可以代表一个目录结构或一 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 
}

4、文件拷贝

我们可以使用 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' 
        } 
    } 
}

5、归档文件

通常一个项目会有很多的 Jar 包,我们希望把项目打包成一个 WAR,ZIP 或 TAR 包进行发布,这时我们就可以使用 Zip,Tar,Jar,War 和 Ear 任务来实现,不过它们的用法都一样,所以在这里我只介绍 Zip 任务的示例。 首先,创建一个 Zip 压缩文件,并指定压缩文件名称,如下代码所示:

apply plugin: 'java' 
version=1.0 
task 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 压缩包的文 件树对象,示例代码如下

// 使用zipTree 
FileTree zip = zipTree('someFile.zip') 
// 使用tarTree 
FileTree tar = tarTree('someFile.tar')

2、 Dependencies

1. 依赖的方式

Gradle 中的依赖分别为 直接依赖,项目依赖,本地 jar 依赖

dependencies { 
    //①.依赖当前项目下的某个模块[子工程] 
    implementation project(':subject01')
    //②.直接依赖本地的某个jar文件 
    implementation files('libs/foo.jar', 'libs/bar.jar') 
    //②.配置某文件夹作为依赖项 
    implementation fileTree(dir: 'libs', include: ['*.jar']) 
    //③.直接依赖 
    implementation 'org.apache.logging.log4j:log4j:2.17.2' 
}

直接依赖: 在项目中直接导入的依赖,就是直接依赖

implementation 'org.apache.logging.log4j:log4j:2.17.2' 上面是简写法,完整版写法如下: implementation group: 'org.apache.logging.log4j', name: 'log4j', version: '2.17.2'

group/name/version 共同定位一个远程仓库,version 最好写一个固定的版本号,以防构建出问题,implementation 类似 maven 中的依赖的 scope,对比 maven 中的依赖:

<dependencies> 
    <dependency> 
        <groupId>log4j</groupId> 
        <artifactId>log4j</artifactId> 
        <version>1.2.12</version> 
        <scope>compile</scope> 
    </dependency> 
</dependencies>

项目依赖:  从项目的某个模块依赖另一个模块

implementation project(':subject01')

这种依赖方式是直接依赖本工程中的 libary module,这个 libary module 需要在 setting.gradle 中配置。

本地 jar 依赖:本地 jar 文件依赖,一般包含以下两种方式

//直接依赖某文件 
implementation files('libs/foo.jar', 'libs/bar.jar') 

//配置某文件夹作为依赖项 
implementation fileTree(dir: 'libs', include: ['*.jar'])

2、依赖的下载

当执行 build 命令时,gradle 就会去配置的依赖仓库中下载对应的 Jar,并应用到项目中。

3、依赖的类型

image.png

4、依赖冲突及解决方案

依赖冲突是指 “在编译过程中, 如果存在某个依赖的多个版本, 构建系统应该选择哪个进行构建的问题”,如下所示:

image.png

A、B、C 都是本地子项目 module,log4j 是远程依赖。

编译时: B 用 1.4.2 版本的 log4j,C 用 2.2.4 版本的 log4j,B 和 C 之间没有冲突 打包时: 只能有一个版本的代码最终打包进最终的A对应的jar |war包,对于 Gradle 来说这里就有冲突了

默认下,Gradle 会使用最新版本的 jar 包【考虑到新版本的 jar 包一般都是向下兼容的】,实际开发中,还是建议使用官方自带的这种解决方案。当然除此之外,Gradle 也为我们提供了一系列的解决依赖冲突的方法: exclude 移除一个依赖,不允许依赖传递,强制使用某个版本

  • Exclude 排除某个依赖
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'){ 
        //排除某一个库(slf4j)依赖:如下三种写法都行 
        exclude group: 'org.slf4j' 
        exclude module: 'slf4j-api' 
        exclude group: 'org.slf4j',module: 'slf4j-api' 
    }
    //排除之后,使用手动的引入即可。 
    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'){ 
        //不允许依赖传递,一般不用 
        transitive(false) 
    }
    //排除之后,使用手动的引入即可 
    implementation 'org.slf4j:slf4j-api:1.4.0' 
}

在添加依赖项时,如果设置 transitive 为 false,表示关闭依赖传递。即内部的所有依赖将不会添加到编译和运行时的类路径。

拓展: 我们可以先查看当前项目中到底有哪些依赖冲突:

//下面我们配置,当 Gradle 构建遇到依赖冲突时,就立即构建失败 
configurations.all() { 
    Configuration configuration -> 
    //当遇到版本冲突时直接构建失败 
    configuration.resolutionStrategy.failOnVersionConflict() 
}

//下面我们配置,当 Gradle 构建遇到依赖冲突时,就立即构建失败 
configurations.all() { 
    Configuration configuration -> 
    //当遇到版本冲突时直接构建失败 
    configuration.resolutionStrategy.failOnVersionConflict() 
}