Gradle简介和使用

作者: 越长越圆 | 来源:发表于2016-09-30 17:20 被阅读889次

    Gradle的使用

    个人博客 http://blog.csdn.net/qq_22329521/article/details/52712705
    内容来自实战Gradle,提供的例子https://github.com/bmuschko/gradle-in-action-source
    Gradle包装器
    --
    在机器上没有安装Gradle运行时的情况下运行Gradle构建,那就是:Gradle包装
    。它也是让构建脚本运行在一个指定的Gradle版本上,它通过自动从中心仓库下载Gradle运行,解压和使用来实现。最终效果是是创建一个和系统无关的gradle版本,而且统一项目所使用的Gradle版本。

    在项目中配置包装器,你只需要做两件事情:创建一个包装器任务和执行任务生成包装器文件。为了能够让项目可以下载压缩过的Gradle运行时文件,定义一个类型为 Wrapper 的任务,通过gradleVersion属性指定你想要使用的Gradle版本:

    //在build.gradle 下创建
    task wrapper(type:Wrapper){
      gradleVersion='1.7'
    }
    /*运行gradle warpper 命令, 会下载 到gradle/wapper 目录下
    gradle-wrapper 是Gradle包装器微类库包含下载和解包Gradle运行时的逻辑
    gradle-wrapper.properties 是包装器元信息包含已下载Gradle运行时的储存位置
    
    和原始URL*/
    

    使用包装器
    在项目下找到gradle.bat 。使用它运行构建和使用已安装的Gradle运行时运行构建是一样的
    定制包装器
    某些企业有非常严格的安全策略,特别是当你为政府机构工作时,访问外网是禁止的。在这种情况下,如何让你的项目使用Gradle包装器呢?重新配置。你可以改变默认配置,将它指向一个存有运行时发布文件的企业内部服务器。同时你还可以改变本地的存储路径

    task wrapper(type:Wrapper){
      gradleVersion = '1.2'        //Gradle版本号
      distributionUrl = ' //获取Gradle包装器的URL 
      distributionPath = 'gradle-dists'  //包装器被解压缩后存放的相对路径
    }
    

    Gradle基本原理

    每个Gradle构建都包含三个基本构建快:project、task和property。每个构建块都包含至少一个project,进而又包含一个或多个task。project和task暴露的属性可以用来控制构建。
    project
    --
    一个项目(project)代表一个正在构建的组件(比如,一个JAR文件),或一个想要完成的目标,如部署应用程序。每个Gradle构建脚本至少定义一个项目。当构建进程启动后,Gradle基于build.gradle中的配置实例化org.gradle.api.Project类,并且能够通过project变量使其隐式可用。Gradle构建的主要入口点


    project

    一个project可以创建新的task,添加依赖关系和配置,并应用插件和其他的构建脚本。它的许多属性可以入name 和description能通过get和set方法访问

    version = '0.1-SNAPSHOT'
    
    task printVersion {
        group = 'versioning'
        description = 'Prints project version.'
    
        doLast {
            logger.quiet "Version: $version"
        }
    }
    
    //在不显示使用project变量的情况下设置项目描述
    

    Task重要功能

    1 . 任务动作:定义一个当前任务执行时最小的工作单元。可以简单的打印“Hello World"也可以编译Java源代码
    2 . 任务依赖: 将一个任务运行时需要另一个task的输出作为输入来完成自己的动作

    `task

    每个Task和Project实例都提供通过getter和setter方法访问的属性。也支持扩展属性

    project.ext.myProp = 'myValue'
    
    ext {
        someOtherProp = 123
    }
    
    assert myProp == 'myValue'
    println project.someOtherProp
    ext.someOtherProp = 567
    
    //可支持在gradle.properties文件中声明然后进行访问
    

    声明Task

    version = '0.1-SNAPSHOT'
    
    task first << { println "first" }
    task second << { println "second" }
    
    task printVersion(dependsOn: [second, first]) << {
        logger.quiet "Version: $version"
    }
    
    task third << { println "third" }
    third.dependsOn('printVersion')     
    
    <<是doLast的快捷版本 dependsOn 是处理task的依赖关系
    

    终结器(相当于finally)

    task first << { println "first" }
    task second << { println "second" }
    first.finalizedBy second
    

    添加任意代码和配置

    
    ext.versionFile = file('version.properties')//提供file方法 
    //此处没有>>这种符号 是task配置模块
    //gradle的声明周期是 初始化阶段,配置阶段,执行阶段
    task loadVersion {
        project.version = readVersion()//最终调用toString的方法
    }
    
    ProjectVersion readVersion() {
        logger.quiet 'Reading the version file.'
    
        if (!versionFile.exists()) {
            throw new GradleException("Required version file does not exist: $versionFile.canonicalPath")
        }
    
        Properties versionProps = new Properties()
    
        versionFile.withInputStream { stream ->//读取InputStream
            versionProps.load(stream)
        }
    
        new ProjectVersion(versionProps.major.toInteger(), versionProps.minor.toInteger(), versionProps.release.toBoolean())
    }
    
    task printVersion << {
        logger.quiet "Version: $version"
    }
    
    class ProjectVersion {
        Integer major
        Integer minor
        Boolean release
    
        ProjectVersion(Integer major, Integer minor) {
            this.major = major
            this.minor = minor
            this.release = Boolean.FALSE
        }
    
        ProjectVersion(Integer major, Integer minor, Boolean release) {
            this(major, minor)
            this.release = release
        }
    
        @Override
        String toString() {
            "$major.$minor${release ? '' : '-SNAPSHOT'}"
        }
    }
    

    Input和Output

    task makeReleaseVersion(group: 'versioning', description: 'Makes project a release version.') {
        inputs.property('release', version.release)
        outputs.file versionFile
    
        doLast {
            version.release = true
            ant.propertyfile(file: versionFile) {
                entry(key: 'release', type: 'string', operation: '=', value: 'true')
            }
        }
    }
    
    

    自定义Task

    class ReleaseVersionTask extends DefaultTask {
        @Input Boolean release //通过注解来声明task的输入和输出
        @OutputFile File destFile
    
        ReleaseVersionTask() {
            group = 'versioning'
            description = 'Makes project a release version.'
        }
    
        @TaskAction
        void start() {
            project.version.release = true
            ant.propertyfile(file: destFile) {
                entry(key: 'release', type: 'string', operation: '=', value: 'true')
            }
        }
    }
    
    

    依赖管理

    不完善的依赖管理技术

    • 手动将JAR文件拷贝到开发机器上。这种处理依赖关系是最原始、非自动化而且最容易出错的方式
    • 使用JAR文件共享存储器(比如,一个共享网络驱动器的文件夹),它被安装在开发人员的机器上,或者通过FTP检索二进制文件。这种方式需要开发人员建立二进制仓库的连接。新的依赖关系需要在有写权限和访问许可的情况下手动进行添加。
    • 检查所下载的JAR文件,使其和版本控制系统中的项目源代码符合。这种方法不需要任何额外的设置,只需要将源代码和依赖绑定在一起。当更新仓库中的本地副本时,整个团队都可以检索到变化。缺点是一些不必要的二进制文件占用了仓库的大量空间。只要源代码发生变化。类库副本的变化就需要频繁地检入。如果你正在开发的多个项目彼此互相依赖,就更是如此了.

    外部依赖

    //自定义配置,通过名字定义一个新的配置
    configurations {
        cargo {
            description = 'Classpath for Cargo Ant tasks.'//设置描述
            visible = false//设置描述是否可见
        }
    }
    ext.cargoGroup = 'org.codehaus.cargo'
    ext.cargoVersion = '1.3.1'
    
    //依赖声明中使用map形式包含group、name和version属性
    dependencies {
        cargo group: cargoGroup, name: 'cargo-core-uberjar', version: cargoVersion
        cargo "$cargoGroup:cargo-ant:$cargoVersion"
    }
    
    repositories {
        mavenCentral()
    }
    

    排除传递依赖如果遇到维护性很差的依赖,可以完全控制传递性依赖,有选择的排除某些依赖

    dependencies {
        cargo('org.codehaus.cargo:cargo-ant:1.3.1') {
            exclude group: 'xml-apis', module: 'xml-apis'
        }
        cargo 'xml-apis:xml-apis:2.0.2'
    }
    
    //排除所以传递性的依赖
    dependencies {
        cargo('org.codehaus.cargo:cargo-ant:1.3.1') {
            transitive=false
        }
    }
    

    动态版本声明

    //声明最新版本的依赖
    dependencies {
        cargo 'org.codehaus.cargo:cargo-ant:1.+'
    }
    

    少用。可靠和可重复的构建才是最重要的

    文件依赖

    //从仓库中获取以来然后拷贝到用户home目录下的libs/cargo子目录
    task copyDependenciesToLocalDir(type: Copy) {
        from configurations.cargo.asFileTree
        into "${System.properties['user.home']}/libs/cargo"
    }
    //将文件以来到项目中
    dependencies {
        cargo fileTree(dir: "${System.properties['user.home']}/libs/cargo", include: '*.jar')
    }
    

    仓库

    仓库分为 Maven仓库 lly仓库和扁平的目录查看
    Maven仓库
    --
    Maven仓库是java项目中最常用的仓库类型之一。类库通常是以JAR文件的形式表现。
    Maven Central是构建中经常使用的仓库。Gradle想尽可能地被构建开放人人员使用,因此Gradle提供了一种快捷方式来声明Maven Central.而不必每次要定义URL http://repo1.maven.org/maven2 ,可以直接调用mavenCentral()方法

    repositories {
        mavenCentral()
    }
    

    当Gradle获取以来时,它会定位仓库,下载并储存在本地缓存中。这个缓存在本地文件系统中的位置与Maven储存所下载的工作的目录是不同的。如果你在处理一个使用maven生成类库的项目,另一个Gradle项目也想使用这个类库。在开发阶段,需要检查实现周期的变化,并且要应用这些变化,为了防止每次小小的改动都必须将类库发布到远程maven仓库,Gradle 提供了本地maven仓库

    repositories {
        mavenLocal()
    }
    

    自定义仓库

    repositories {
        maven {
            name 'Custom Maven Repository'
            url 'http://repository-gradle-in-action.forge.cloudbees.com/release/'
        }
    }
    

    应对版本冲突

    当遇到版本冲突让构建失败,运行项目的任何task都会显示版本冲突信息

    configurations.cargo.resolutionStrategy{
      failOnVersionConflict()
    }
    

    强制指定一个版本

    确保所有项目中使用同一个版本的类库

    configurations.cargo.resolutionStrategy{
      force 'xxxx.1.3.0'
    }
    

    多项目依赖

    allprojects {
        group = 'com.manning.gia'
        version = '0.1'
    }
    
    subprojects {
        apply plugin: 'java'
    }
    
    project(':repository') {
        dependencies {
            compile project(':model')
        }
    }
    
    project(':web') {
        apply plugin: 'war'
        apply plugin: 'jetty'
    
        repositories {
            mavenCentral()
        }
    
        dependencies {
            compile project(':repository')
            providedCompile 'javax.servlet:servlet-api:2.5'
            runtime 'javax.servlet:jstl:1.1.2'
        }
    }
    

    单元测试

    JUnit测试

    public class InMemoryToDoRepositoryTest {
        private ToDoRepository inMemoryToDoRepository;
    
        @BeforeClass
        public void setUp() {
            inMemoryToDoRepository = new InMemoryToDoRepository();
        }
    
        @Test
        public void testToDoItem() {
            ToDoItem newToDoItem = new ToDoItem();
            newToDoItem.setName("Write unit tests");
            Long newId = inMemoryToDoRepository.insert(newToDoItem);
            assertNotNull(newId);
    
            ToDoItem persistedToDoItem = inMemoryToDoRepository.findById(newId);
            assertNotNull(persistedToDoItem);
            assertEquals(newToDoItem, persistedToDoItem);
        }
    }
    
    dependencies {
        testCompile 'org.testng:testng:6.8'
    }
    
    

    代码质量管理和检测

    监测代码质量不只有代码覆盖率分析。每个团队或者组织都有自己的代码规范标准,这个标准包括从简单的代码格式方面,如空格的使用和缩进,到编程最佳实践。遵循这些规范,会使代码对于其他团队成员来说更加可读,从而提高其可维护性。
    在java中,可以使用广泛的开源工具或者商业解决方案,比如Checkstyle,PMD,Cobertura,FindBugs和Sonar。大多数这些工具都已Gradle核心插件或者第三方插件来使用。并且无缝地集成到构建中
    代码分析工具的几个度量度

    • 代码覆盖率
    • 代码标准的遵守情况
    • 不好的编码实践和设计问题
    • 过度复杂、重复、强耦合的代码

    代码覆盖率

    覆盖率的标准

    • 分支覆盖率:衡量可能被执行的分支(如if/else分支逻辑)的执行情况
    • 语句覆盖率:衡量代码块中那些语句被执行了
    • 方法覆盖率:衡量执行测试的时候进入了哪些方法
    • 复杂度度量:包、类和方法的圈复杂度(代码块中独立的分支数)
      检测代码覆盖率的工具如 JacCoCo
    buildscript {
        repositories {
            mavenCentral()
        }  
    dependencies {
    //从MavenCentral获取JaCoCo插件添加到构建脚本的classpath中
            classpath 'org.ajoberstar:gradle-jacoco:0.3.0'
        }
    }
    apply plugin: org.ajoberstar.gradle.jacoco.plugins.JacocoPlugin//按类型应用插件
    jacoco {
        integrationTestTaskName = 'integrationTest'//定义集成测试的task名字,用于为集成测试 生成代码覆盖率报告
    }
    

    在其他项目中引入这个脚本插件。在构建后有个html文件就可以查看

    allprojects {
        apply plugin: 'idea'
        group = 'com.manning.gia'
        version = '0.1'
    }
    subprojects {
        apply plugin: 'java'
        apply from: "$rootDir/gradle/jacoco.gradle"
    
        repositories {
            mavenCentral()
        }
    }
    

    参考文章
    实战Gradle
    https://my.oschina.net/fhd/blog/522382?

    相关文章

      网友评论

      • 鬼兜子:同学 哈哈 你的gradle版本也太老了吧

      本文标题:Gradle简介和使用

      本文链接:https://www.haomeiwen.com/subject/unrhyttx.html