24 七月 2017

前言

本文没有 gradle 与 maven 对比,也没有深入 gradle 实现。只有 gradle 从 0 开始使用的说明。

近期的相关文章:

一个多模块的 java 项目的配置的实践:

如何理解一个 gradle 构建脚本

简介

gradle 是一个构建工具。负责管理项目依赖,组织项目结构,完成项目构建的工作。

安装

从 官网 下载,解压即可,并将 bin/gradle 命令加入到 path 中。

打开一个 gradle 项目

通常, 大家首次接触 gradle 是遇到一个 gradle 作为构建工具项目.

观察基于 gradle 构建的项目结构, 通常有以下几个文件:

build.gradle                      // 重要, 构建脚本文件,主要的构建配置都在这里写
gradlew                           // gradle wrapper 执行脚本文件,用来在没有安装 gradle 的情况下执行 gradle 命令。当然,第一次执行时会下载 gradle。
gradlew.bat                       // gradle wrapper 执行脚本文件的 windows 版
gradle
|- wrapper
   |- gradle-wrapper.properties   // 重要, 描述了 gradlew 使用的 gradle 版本
   |- gradle-wrapper.jar          // gradlew 执行时用到的 jar, 无需关注
settings.gradle                   // 项目配置,指明根项目名字和引入的 module

执行下

./gradlew tasks

能够列出所有可用的构建命令. 这时如果是第一次运行 gradle, 且本地没有缓存过 gradle-wrapper.properties 中描述的 gradle 版本, 则会去 gradle-wrapper.properties 指的的 url 去下载 gradle.

但是这个官方地址的 gradle 下载通常会比较慢, 所以如果是要给同事分享 gradle 项目时, 可以把 gradle 提前下载到内网, 然后将 gradle-wrapper.properties 中的下载地址改成内网的地址.

使用 idea 打开这个项目, 也就是 open build.gradle 文件, 就可以引入项目了.

新建一个 gradle 项目

  1. 新建一个文件夹作为项目文件夹

    ```
    mkdir project-example
    cd project-example
    ```
  2. 使用 gradle 初始化

    ```
    gradle init
    ```
    然后目录下会生成这些文件
    ```
    build.gradle     // 构建脚本文件,主要的构建配置都在这里写
    gradle           // 存放gradle wrapper 执行配置和工具的文件夹,
    gradlew          // gradle wrapper 执行脚本文件,用来在没有安装 gradle 的情况下执行 gradle 命令。当然,第一次执行时会下载 gradle。
    gradlew.bat      // gradle wrapper 执行脚本文件的 windows 版
    settings.gradle  // 项目配置,指明根项目名字和引入的 module
    ```
  3. 修改 build.gradle 文件,结果像下面这样

    ```
    /*
    声明插件,声明插件的方式有两种,下面这种是 2.* 版本之后的新的定义方式。可以声明两种插件:
    1. 官方内置插件,如 java,idea。
    2. 在 gradle 插件中心注册过的插件,如第三个就是,这种要还要声明版本。
    声明插件的目的是因为插件会提供很多 task,而每一个 task 就是一段构建脚本,可以执行不同的任务。比如 compileJava,test 等。
    */
    plugins {
        id 'java'
    }
    /*
    声明 group 和 版本,而 project 的名字在 settings.gradle 文件中,而子 module ,或者又称为 sub projects 的名字通常和文件夹名字一样。
    */
    group 'com.yangxiaochen.gradle'
    version '1.0.0-SNAPSHOT'
    /*
    声明依赖仓库,这里使用的是 jcenter,也可以使用其他的如 mavenCentral 等。也可以声明多个,会按顺序查找。
    */
    repositories {
        jcenter()
    }
    /*
    声明依赖
    */
    dependencies {
        compile 'org.slf4j:slf4j-api:1.7.21'
        testCompile 'junit:junit:4.12'
    }
    ```
       编辑 settings.gradle 文件如下,声明了project name
       ```
       rootProject.name = 'project-example'
       // include 'sub-project'
       ```
    4. 用 idea open build.gradle 文件.

常见问题

插件选择

内置插件都在官方文档中有说明,除了内置插件外, 可以 [Search Gradle plugins](https://plugins.gradle.org/),gradle 插件的官方仓库。

build速度慢

速度慢一般来说是依赖更新慢,因为众所周知的原因,国内使用国外的仓库速度并不乐观,所以可以替换`repositories`, 使用阿里云的仓库

repositories {
  maven {
  	url "http://maven.aliyun.com/nexus/content/groups/public"
  }
}

再者, 有的同学使用私服, 可能是公司搭建的. 这个有时候会有不稳定, 且有时需要认证的情况. 看稍后的章节.

如何debug

gradle 运行中的提示并不算很友好, 尤其在链接仓库和下载jar包时, 在连接有问题的时候, 经常没有提示(新版本的可能好一些.)

经常使用

gradle idea -i
gradle idea -id

还可以加入 --no-daemon 参数来避免daemon启动产生的日志干扰
gradle idea -id --no-daemon
一般来说就能定位到构建问题.

来获取更多的信息. 比较常见的卡住是因为`仓库链接不上`, jar包下载链接不上, 又不返回 response (私有仓库偶尔有问题), 资源下载缓慢, 仓库需要认证登陆

  • 仓库连接不上: 检查仓库地址, 检查网络.

  • 资源无法连接又没有response: 这种通常会在上面打出的日志中体现, 请求一个 url 便没有响应.

  • 下载缓慢: 更换仓库, 或者忍耐下第一次, 以后会好很多

在 IDEA 刷新有问题时, 可以执行这个命令debug, 一般这个命令能成功, 那么 IDEA 刷新通常也能成功. (请注意版本问题, 命令行里执行的 gradle 版本需要跟 IDEA 里配置的 gradle 版本一致)

私服使用和包管理混乱引起的问题

私服使用中会有以下问题:

  1. 不稳定, 偶尔服务无响应.

  2. 配置出错, 无法 proxy maven 主仓库. 或者出现私服中存在第三方包但是却不完整到时无法下载.

    一般来说会尝试把第三方仓库配置到私服之前:
    ```
    maven {
      url "http://maven.aliyun.com/nexus/content/groups/public"
    }
    maven {
      url "http://你家私服地址/"
      credentials {
        username "xxx"
        password "xxx"
      }
      authentication {
      	basic(BasicAuthentication)
      }
    }
    ```
    这样先去第三方查找, 再到私服查找.
  3. 私服有认证. 可以参照第二条私服的认证方式. 其中

    ```
      authentication {
      	basic(BasicAuthentication)
      }
    ```
    是可选填的, 不填的话会自动识别认证方式.
  4. 打包方式不完整. 有的同学上传包的时候虽然包含了编译后的jar包, pom文件, 但是有时却没有把source包上传, gradle 默认是会下载source的, 如果没有, 私服返回404还好, 最怕会卡主(遇到过私服虽然没有这个文件却迟迟不返回response的情况.), 所以, 如果遇到这种问题, 可以:

    ```
    idea {
    	module {
    		downloadJavadoc = false
    		downloadSources = false
    	}
    }
    ```
    不让 IDEA 刷新的时候下载源码和文档, 万不得已还是不要用.

常用命令或参数

--refresh-dependencies 刷新依赖,刷新那些SNAPSHOT的依赖,类似于 maven 的 -U 参数

gradle tasks 列出所有可执行的task

gradle tasks --all 列出所有可执行的task, 并附加上 mudole

gradle help --task taskName 查看一个task的帮助, 不过并不是所有task都有, 因为task都是可以自己来实现的.

gradle idea -id 用来debug IDEA进行刷新时遇到的问题, 一般这个任务能成功, 刷新就能成功. 注意使用的 gradle 版本要一致.

--no-daemon 不是使用daemon进行构建. daemon的作用是不用每次 build 都再启动一个进程, 节省时间. 但却会在我们 debug 问题的时候不停的生产日志, 产生干扰.

依赖冲突处理

可以通过命令来查看一个项目的依赖

gradle webapp:dependencies --configuration=compile

不同于maven的最近原则,gradle 依赖使用的是最新原则. 比如你构建处于依赖顶层的 module , 那么版本会优先使用这个顶层module 的`build.grale` 中定义的版本. 所以可以在顶层module 中强制定义版本

依赖排除, 通常可以使用

compile("org.springframework.boot:spring-boot-starter-web") {
    exclude module: "spring-boot-starter-logging"
}

这样的语法, 还有更为粗暴直接的:

configurations {
    all*.exclude group: 'ch.qos.logback', module: 'logback-classic'
    all*.exclude group: 'ch.qos.logback', module: 'logback-core'
}

参考资料

官方文档相当的详细,且附有很多example,配合 gradle 安装包里的 example 源文件阅读