hive compile 源码编译学习随笔

编译的源码来源于什么地方?如何知道互相兼容的版本号?用什么工具编译?和辅助的工具有哪些?编译有bug时如何快速定位到问题? 如果解决问题;我们第一点的基本思路是: ”先明确hive的版本,同时希望这个版本的hive去兼容某个版本的Hadoop“

1、一些问题

  • 编译的源码来源于什么地方?
  • 如何知道互相兼容的版本号?
  • 用什么工具编译?和辅助的工具有哪些
  • 编译有bug时如何快速定位到问题? 如果解决问题

2、编译的版本

Hadoop: 3.1.3
hive : 3.1.3
spark: 3.1.3

* 为什么都选择3.1.3,在hive 的官方地址,是有如下图说明的,那我们就默认hive3.1.3和hadoop3.1.3是完全兼容的(实际上是有bug的);以这两个版本的Hadoop和hive搭建的集群在启动hive的时候会报兼容性错误。

hive文档说明

实际上,我们要编译的组件一般不会是hadoop ,多数情况是hive,spark等组件,主要是因为这些组件如果基于Hadoop生态的运行的话,那都是这些组件去兼容Hadoop,故我们这里是主要编译hive。

也就是我们第一点的基本思路是: ”先明确hive的版本,同时希望这个版本的hive去兼容某个版本的Hadoop

3、寻找源码

源码基本上都是去GitHub上的maven仓库里找

地址:https://github.com/apache/hive 或者 https://gitee.com/apache/hive

此地址看到的hive的master分支,我们可以点击master的下拉框,然后输入我们的版本号,注意我们要编译的是具体的版本不是分支,所以要搜索tags,搜索后如下图。这个时候其实主要是搜一下看看有没有我们要编译的版本以及该版本下对应的其它组件版本号

切换到该版本后,找pom.xml文件,点击打开;我们可以直接在打开的页面搜索 hadoop ,多找一下就会发现hive3.1.3对应的Hadoop版本其实是 <hadoop.version>3.1.0</hadoop.version>,也就是我们之前所说的,如果把hive3.1.3放在hadoop3.1.3上搭建环境,大概率会出现问题的。

github选择hive3.1.3

4、配置环境

  • MacBook Pro (13-inch, M1, 2020), macOS Monterey
  • java: jdk1.8.0_212
  • maven: Apache Maven 3.8.6
  • IntelliJ IDEA 2022.2.2 (Community Edition)
  • git version 2.37.3

4.1 idea git 插件下载源码

  • idea 打开后 通过 Get from CVS的方式,填入github上hive的地址
  • 下载后在idea的右下角master的地方点一下,然后点击 。 checkout tag or version,输入3.1.3我们需要的版本点击确定。则branch就切换到该版本了。
  • 无论是 master分支还是我们想要切换的分支遇到提示 。 reload maven projects 的弹出框都点击yes
  • 此时刷新maven插件,基本上大概率是会报错的。不管是 pentaho-aggdesigner-algorithm:pom:5.1.5-jhyde 和ldap-client-api:pom的哪一个错误,我们去搜索资料都能找到一些解决方案,但是基本上都是不可用的。
  • 为什么说报错在此篇文章书写时没法解决,主要是因为maven的问题,maven此时是最新版,而且从3.8.1开始maven block掉了所有HTTP协议的repositories,所以针对前边提到的各种错误,不管搜到什么答案,大概率都是没法有效解决的。笔者的处理方案是手动下载一个maven的tar包,配置一个maven3.6.3的版本,也就是次新的大版本
  • 刷新maven插件,基本上不会报错了;在Terminal输入 。 mvn clean package -Pdist -DskipTests -Dmaven.javadoc.skip=true,回车编译来验证当前的3.1.3版本是否有问题。

4.2 遇到的错误

Could not find artifact org.pentaho:pentaho-aggdesigner-algorithm:pom:5.1.5-jhyde in nexus-aliyun
网上多数说是增加阿里云的spring- plugin的依赖,但是实际上没有效果,主要是此时的spring- plugin依赖里也没有这个jar包,需要增加spring自身的仓库支持
https://www.codeleading.com/article/91364854522/
5.1.5-jhyde
Failure to find org.glassfish:javax.el:pom:
此问题可以在此处不关注它,
glassfish:javax.el:pom
idea maven 一直报错“Could not transfer artifact
也可以不关注此问题,但是有专门的此问题场景解决方案
https://blog.51cto.com/u_15162069/2804511
Could not transfer artifact
Blocked mirror for repositories
最新版本的maven block掉了所有HTTP协议的repositories,仅支持https;maven次新大版本是3.6.3,是不会出现这个问题的。所以手动降版本,这也一下解决好几个问题
https://www.jianshu.com/p/274a45a2db05https://blog.csdn.net/qq_41980563/article/details/122061818
Blocked mirror for repositories

5、修改Hadoop版本号及处理报错

  • 视频教程里 : https://www.bilibili.com/video/BV1x14y177Ab?p=7&vd_source=b2e82cf2c96f0ecd7432e09d95d7d8ec 直接在集群中调用hive触发里问题,从而快速的找到了主要兼容的jar包,但是一般我们习惯性直接将hive源码的pom文件hadoop设置成我们希望的版本 3.1.3,直接编译,笔者尝试过,直接改 Hadoop3.1.2 这样编译时不出现问题。
  • 笔者并没有完全直接去修改主要的兼容报错的jar包, 在编译时遇到了各种报错,笔者没有采用视频摘樱桃的方式,而是逐一来修改涉及到的代码
  • java的某个类或者方法开始计算停用的时候会在官方API文档中开始做上 。 Deprecated 标记,有这个单词的方法说明官方在通知各位这个方法要做好放弃的准备了,它一般也同时给出解决方案和停用大概时间

6、修改的java文件及主要问题

  • 组件版本升级时涉及大到代码适配的工作,主要就是针对代码中的错误,去定位对应包中类的方法是在什么时候做出来了改变,而官方的API文档中都会有相应的痕迹,耐心的去文档里大版本的对应着找一找就可以找到了。(所谓大版本,就是类似于1.0,2.0,而中间的1.01,1.02 这些可以暂时跳过,除非某两个大版本相应的方法直接发生了变化,这时需要细化中间的小版本来看一下具体的情况,但一般都是大版本发布某方法要被弃用了,替换了的声明)
  • 有时候,你不需要去懂怎么回事,只要找到关键位置,照葫芦画瓢的做就好了
  • 其实要修改的还有很多地方,因为hive有很多的 module,每个module都可能指定不同的版本,而只有在某些特定场运行时的场景下才可能触发bug,但是现在编译确实是能通过。其它具体的问题就得去进一步慢慢验证修复
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.6.1:compile (default-compile) on project hive-druid-handler: Compilation failure[ERROR] /Users/ethan/IdeaProjects/hive/druid-handler/src/java/org/apache/hadoop/hive/druid/serde/DruidScanQueryRecordReader.java:[46,61] <T>emptyIterator()在com.google.commn.collect.Iterators中不是公共的; 无法从外部程序包中对其进行访问
import java.util.Collections;// 替换了如下的代码// private Iterator<List<Object>> compactedValues = Iterators.emptyIterator(); private Iterator<List<Object>> compactedValues = Collections.emptyIterator();
此问题找解决方法的过程是视频中说的方式,去API文档找报错的方法是在什么时候开始过渡停用的。整个修改的思路是差不多的,单独列出来这个是除了这个类,其它的修改基本上都是一样的方法,一样的处理方式
Iterators中不是公共的
git status刷新索引: 100% (17020/17020), 完成.位于分支 my-hive-3.1.3尚未暂存以备提交的变更: (使用 “git add <文件>…” 更新要提交的内容) (使用 “git restore <文件>…” 丢弃工作区的改动) 修改: druid-handler/pom.xml 修改: druid-handler/src/java/org/apache/hadoop/hive/druid/serde/DruidScanQueryRecordReader.java 修改: llap-common/src/java/org/apache/hadoop/hive/llap/AsyncPbRpcProxy.java 修改: llap-server/src/java/org/apache/hadoop/hive/llap/daemon/impl/AMReporter.java 修改: llap-server/src/java/org/apache/hadoop/hive/llap/daemon/impl/LlapTaskReporter.java 修改: llap-server/src/java/org/apache/hadoop/hive/llap/daemon/impl/TaskExecutorService.java 修改: llap-tez/src/java/org/apache/hadoop/hive/llap/tezplugins/LlapTaskSchedulerService.java 修改: pom.xml 修改: ql/src/java/org/apache/hadoop/hive/ql/exec/tez/WorkloadManager.java 修改: ql/src/test/org/apache/hadoop/hive/ql/exec/tez/SampleTezSessionState.java
以上是所有修改的地方,pom文件的修改主要是修改guava的版本号
修改guava的版本号

7、替换spark

先修改pom文件,替换spark的版本,spark3.x 对应的Scala版本最低是2.12,所以同时把Scala的版本也修改掉,可能有读者会问,怎么知道Scala也得变的,不要etc,自己去官方文档看一下基本的要求就知道了。

<spark.version>3.1.3</spark.version><scala.binary.version>2.12</scala.binary.version><scala.version>2.12.10</scala.version><SPARK_SCALA_VERSION>2.12</SPARK_SCALA_VERSION>

然后继续在Terminal通过命令: mvn clean package -Pdist -DskipTests -Dmaven.javadoc.skip=true, 来从新编译,笔者遇到了大致三个问题

7.1 SparkCounter 中累加器的修改

这里主要是设计到spark自定义累加器实现的继承类替换问题。具体远离参考 spark 累加器学习随笔

7.2 ShuffleWriteMetrics.java 报错

metrics.shuffleWriteMetrics().ShuffleBytesWritten()不存在,从名称看类似的方法为bytesWritten()metrics.shuffleWriteMetrics().ShuffleWriteTime()同样不存在,修改为writeTime()
public ShuffleWriteMetrics(TaskMetrics metrics) { // metrics.shuffleWriteMetrics().ShuffleBytesWritten()不存在,从名称看类似的方法为bytesWritten() // metrics.shuffleWriteMetrics().ShuffleWriteTime()同样不存在,修改为writeTime() this(metrics.shuffleWriteMetrics().bytesWritten(), metrics.shuffleWriteMetrics().writeTime()); }
ShuffleBytesWritten()不存在

7.3 找不到spark_project

编译报错提示org.spark_project.guava····· 不存在,找到相关报错的java文件,修改import org.spark_project.guava.collect.Sets;为 import org.sparkproject.guava.collect.Sets;。保存后,重新安装依赖

spark Accumulator AccumulatorV2 累加器学习随笔

spark Accumulator AccumulatorV2 累加器是Spark的核心数据结构之一 — Spark的三大核心数据结构:RDD、累加器(只写不读)、广播变量(只读不写),累加器在不同的spark版本中有不一样的具体实现逻辑

spark累加器图示

spark Accumulator AccumulatorV2 累加器是Spark的核心数据结构之一 — Spark的三大核心数据结构:RDD、累加器(只写不读)、广播变量(只读不写),累加器在不同的spark版本中有不一样的具体实现逻辑;而累加器的基本逻辑过程如下

  1. 自定义变量在Spark中运算时,会从Driver中复制一份副本到Executor中运算,但变量的运算结果并不会返回给Driver,所以无法实现自定义变量的值改变,一直都是初始值,所以针对这个问题,引入了累加器的概念;
  2. 系统累加器longAccumulator和自定义累加器(extends AccumulatorV2[类型,类型])实际都是两步,new累加器,然后sc.register注册累加器;
  3. 先在Driver程序中创建一个值为0或者空的累加器对象,Task运算时,Executor中会copy一份累加器对象,在Executor中进行运算,累加器的运算结果返回给Driver程序并合并Merge,得出累加器最终结果
  4. 累加器.add(元素);具体对元素的操作包括数据sum、增加、删减、筛选等要求,都可以写在自定义累加器的.add()方法中。

Spark API

  • spark API的地址都可以在该网址中找到: https://spark.apache.org/docs/
  • 点击想要看到的版本,页面打导航栏有 API Docs ,点击想要了解的语言名称即可,例如点击 Scala,则进入对应的API地址 : https://spark.apache.org/docs/2.0.0/api/scala/index.html#org.apache.spark.package
  • 我们查看具体的类和方法的时候就特别注意一个单词 【Deprecated】,它有时候会出现类声明的最开始 Annotations,或者直接在具体的方法说明中出现。当出现这个单词的时候就意味着这个类或者方法在以后的版本中要慢慢被弃用或者替代

Accumulator 和 AccumulatorParam

Spark1.x 中实现累加器需要用到类 Accumulator和 AccumulatorParam。以spark1.6.3为例,内置数值类型的累加器用Accumulator类,而自定义累加器需要继承接口AccumulatorParam ,并实现相应的方法,而在2.0版本之后这个方式开始不再推荐使用了。

    // 在类的声明中出现了如下的说明,也就是该类将被 AccumulatorV2 所替代。
    Annotations @deprecated
    Deprecated (Since version 2.0.0) use AccumulatorV2
    trait AccumulatorParam[T] extends AccumulableParam[T, T]

While this code used the built-in support for accumulators of type Int, programmers can also create their own types by subclassing AccumulatorParam. The AccumulatorParam interface has two methods: zero for providing a “zero value” for your data type, and addInPlace for adding two values together. For example, supposing we had a Vector class representing mathematical vectors, we could write:

object VectorAccumulatorParam extends AccumulatorParam[Vector] {
  def zero(initialValue: Vector): Vector = {
    Vector.zeros(initialValue.size)
  }
  def addInPlace(v1: Vector, v2: Vector): Vector = {
    v1 += v2
  }
}

// Then, create an Accumulator of this type:
val vecAccum = sc.accumulator(new Vector(...))(VectorAccumulatorParam)

AccumulatorV2

从spark2.0开始自定义累加器的实现不再提倡使用AccumulatorParam, 而是使用AccumulatorV2, 自定义类继承AccumulatorV2,并重写其固定的几个方法

  • reset:用于重置累加器为0
  • add:用于向累加器加一个值
  • merge:用于合并另一个同类型的累加器到当前累加器
  • copy():创建此累加器的新副本
  • isZero():返回该累加器是否为零值
  • value():获取此累加器的当前值
def main(args: Array[String]): Unit = {
        val conf = new SparkConf().setMaster("local[*]").setAppName("Application")
        //构建Spark上下文对象
        val sc = new SparkContext(conf)

        //创建累加器
        val sum = new MyAccumulator()

        //注册累加器
        sc.register(sum,"accumulator")

        val rdd = sc.makeRDD(Array(1,2,3,4,5))

        rdd.map(item=>{
            sum.add(item)
        }).collect()
        println("sum = "+sum.value)

        //释放资源
        sc.stop()
    }

//自定义累加器
class MyAccumulator extends AccumulatorV2[Int,Int]{
    var sum = 0

    //1. 是否初始状态(sum为0表示累加器为初始状态)
    override def isZero: Boolean = sum == 0

    //2. 执行器执行时需要拷贝累加器对象(把累加器对象序列化后,从Driver传到Executor)
    override def copy(): AccumulatorV2[Int,Int] = {
        val mine = new MyAccumulator
        mine
    }

    //3. 重置数据(重置后看当前累加器是否为初始状态)
    override def reset(): Unit = sum = 0

    //累加数据
    override def add(v: Int): Unit = {
        sum = sum + v
    }

    //合并计算结果数据(把所有Executor中累加器value合并)
    override def merge(other: AccumulatorV2[Int, Int]): Unit = {
        sum = sum + other.value
    }

    //累加器的结果
    override def value: Int = sum
}

gradle idea 基础入门

一、环境参数

  1. MacBook Pro (13-inch, M1, 2020) Apple M1
  2. java version “1.8.0_291”
  3. Gradle 7.5.1
  4. IntelliJ IDEA 2022.2.2 (Community Edition)

二、基本命令

指令 作用
gradle clean 清空build目录
gradle classes 编译业务代码和配置文件
gradle test 编译测试代码,生产测试报告
gradle build 构建项目
gradle build -x test 跳过测试构建构建

三、修改maven下载源

主要通过两个方式修改maven的下载源。

  • 第一种是全局修改,在开发环境中电脑上修改GRADEL_HOME下的配置文件;
  • 第二种是针对单一项目修改maven源

    1、全局修改 – init.gradle文件

    ==在windows环境下的gradle_home 目录下默认有一个init.d的文件夹;而macos中是没有的init.d文件夹,但是不影响整体的配置效果,Macos用户只需要收到创建对应的文件夹即可==

  • 在 gradle 的 init.d 目录下创建以.gradle 结尾的文件,.gradle 文件可以实现在 build 开始之前执行
  • 在 init.d 文件夹创建 init.gradle 文件,文件内容如下
    allprojects { 
    repositories {
        mavenLocal()
        maven { 
            name "Alibaba" ; 
            url "https://maven.aliyun.com/repository/public" 
        } 
        maven { 
            name "Bstek" ; 
            url "https://nexus.bsdn.org/content/groups/public/" 
        } 
        mavenCentral()
    }
    buildscript { 
    repositories {
        maven { 
            name "Alibaba" ; 
            url 'https://maven.aliyun.com/repository/public'
        } 
        maven { 
            name "Bstek" ; 
            url 'https://nexus.bsdn.org/content/groups/public/'
            } 
        maven { 
            name "M2" ; 
            url 'https://plugins.gradle.org/m2/'
        }
    } }
    }

    ==allprojects 是对所有 project(包括 Root Project+ child Project[当前工程和所有子工程])的进行统一配置,而 subprojects 是对所有 Child Project 的进行统一配置。==
    如果是直接在根 project 配置 repositories 和 dependencies 则只针对根工程有效。

  • 启用 init.gradle 文件的方法有以下说明
1.在命令行指定文件,例如:gradle --init-script yourdir/init.gradle -q taskName。你可以多次输入此命令来指定多个init文件 
2.把init.gradle文件放到 USER_HOME/.gradle/ 目录下
3.把以.gradle结尾的文件放到 USER_HOME/.gradle/init.d/ 目录下
4.把以.gradle结尾的文件放到 GRADLE_HOME/init.d/ 目录下
如果存在上面的4种方式的2种以上,gradle会按上面的1-4序号依次执行这些文件,
如果给定目录下存在多个init脚本,会按拼音a-z顺序执行这些脚本,每个init脚本都存在一个对应的gradle实例,
你在这个文件中调用的所有方法和属性,都会 委托给这个gradle实例,每个init脚本都实现了Script接口。
  • 仓库说明
mavenLocal(): 指定使用maven本地仓库,而本地仓库在配置maven时settings文件指定的仓库位置。如E:/repository,gradle 查找jar包顺序如下:USER_HOME/.m2/settings.xml >> M2_HOME/conf/settings.xml >> USER_HOME/.m2/repository
mavenCentral():这是Maven的中央仓库,无需配置,直接声明就可以使用
gradle可以通过指定仓库地址为本地maven仓库地址和远程仓库地址相结合的方式;默认在 USER_HOME/.gradle/caches目录,当然如果我们配置过GRADLE_USER_HOME环境变量,则会放在 GRADLE_USER_HOME/caches目录
那么可不可以将gradle caches指向maven repository。我们说这是不行的,caches下载 文件不是按照maven仓库中存放的方式
  • 阿里云仓库地址请参考:https://developer.aliyun.com/mvn/guide
  • GRALE_USER_HOME 相当于配置 Gradle 本地仓库位置和 Gradle Wrapper 缓存目录。没有配置过 GRALE_USER_HOME 环境变量,默认在当前用户家目录下的.gradle 文件夹中

2、局部修改 – build.gradle 文件

每个gradle项目目录下会有一个build.gradle文件,它可以通过与init.gradle文件一下的配置内容来执行maven的下载源头

  • uild.gradle 是一个 gradle 的构建脚本文件,支持 java、groovy 等语言。
  • 每个 project 都会有一个 build.gradle 文件,该文件是项目构建的入口,可配置版本、插件、依赖库等信息。
  • 每个 build 文件都有一个对应的 Project 实例,对 build.gradle 文件配置,本质就是设置 Project 实例的属性和方法。
  • 由于每个 project 都会有一个 build 文件,那么 Root Project 也不列外.Root Project 可以获取到所有 Child Project,所以在
    Root Project 的 build 文件中我们可以对 Child Project 统一配置,比如应用的插件、依赖的 maven 中心仓库等。

2.1 常见属性

//指定使用什么版本的JDK语法编译源代码,跟编译环境有关,在有java插件时才能用
sourceCompatibility = 1.8 //指定生成特定于某个JDK版本的class文件:跟运行环境有关,在有java插件时才能用
targetCompatibility = 1.8 
//业务编码字符集,注意这是指定源码解码的字符集[编译器] 
compileJava.options.encoding "UTF-8"
//测试编码字符集,注意这是指定源码解码的字符集[编译器] 
compileTestJava.options.encoding "UTF-8"
//编译JA VA文件时采用UTF-8:注意这是指定源码编码的字符集【源文件】 
tasks.withType(JavaCompile) {
    options.encoding = "UTF-8" 
}
//编译JA VA文件时采用UTF-8:注意这是指定文档编码的字符集【源文件】 
tasks.withType(Javadoc) {
    options.encoding = "UTF-8" 
}

2.2 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()
}

2.3 ext 用户自定义属性

//自定义一个Project的属性 
ext.age = 18 
//通过代码块同时自定义多个属性 
ext {
    phone = 19292883833
    address="北京尚硅谷"
}
task extCustomProperty { 
//在task中自定义属性 
    ext {
        desc = "奥利给"
        }
    doLast {
        println "年龄是:${age}" 
        println "电话是:${phone}" 
        println "地址是:${address}" 
        println "尚硅谷:${desc}"
    }
}

2.4 gradle.properties

ext 配置的是用户自定义属性,而 gradle.properties 中一般定义 系统属性、环境变量、项目属性、JVM 相关配置 信息。
详细请参考:https://docs.gradle.org/current/userguide/build_environment.html#sec:gradle_configuration_properties

## 设置此参数主要是编译下载包会占用大量的内存,可能会内存溢出 
org.gradle.jvmargs=-Xms4096m -Xmx8192m
## 开启gradle缓存
org.gradle.caching=true
#开启并行编译 
org.gradle.parallel=true 
#启用新的孵化模式 
org.gradle.configureondemand=true
#开启守护进程 
org.gradle.daemon=true

2.5 Buildscript

  • buildscript 里是 gradle 脚本执行所需依赖,分别是对应的 maven 库和插件
  • buildscript{}必须在 build.gradle 文件的最前端
  • 对于多项目构建,项目的 buildscript()方法声明的依赖关系可用于其所有子项目的构建脚本
  • 构建脚本依赖可能是 Gradle 插件

案例1:

import org.apache.commons.codec.binary.Base64 
buildscript {
    repositories {   
        mavenCentral()
    } 
    dependencies {
        classpath group: 'commons-codec', name: 'commons-codec', version: '1.2
    }
} 

tasks.register('encode') {
    doLast {
        def byte[] encodedString = new Base64().encode('hello world\n'.getBytes()) 
        println new String(encodedString)
    }
}

案例2:

//老式apply插件的引用方式,使用apply+buildscript 
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: 'java' //核心插件,无需事先引入
apply plugin: 'org.springframework.boot' //社区插件,需要事先引入,才能应用,不必写版本号

四、Wrapper 包装器

Gradle Wrapper 实际上就是对 Gradle 的一层包装,用于解决实际开发中可能会遇到的不同的项目需要不同版本的 Gradle 问题;
新建的Gradle项目都会有一个gradle文件夹,如下,它的子目录是wrapper,而wrapper文件夹下有gradle-wrapper.jar和gradle-wrapper.properties两个文件

.
├── build.gradle
├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── src
    ├── main
    │   ├── java
    │   │   └── org
    │   │       └── lifefunker
    │   │           └── Main.java
    │   └── resources
    └── test
        ├── java
        └── resources
  1. 何时使用 gradle wrapper?
    • 本地电脑没有gradle
    • 电脑已有gradle版本过旧
  2. 如何使用 gradle wrapper?
    • 项目中的gradlew、gradlew .cmd脚本用的就是wrapper中规定的gradle版本
    • gradlew、gradlew .cmd的使用方式与gradle使用方式完全一致,只不过把gradle指令换成了gradlew指令
    • gradle指令和gradlew指令所使用的gradle版本有可能是不一样的
  3. 主要参数
参数名 说明
–gradle-version 用于指定使用的gradle版本
–gradle-distribution-url 用于指定下载gradle发行版的url地址

Demo:

  • gradle wrapper –gradle-version=4.4:升级wrapper版本号,只是修改gradle.properties中wrapper版本,未实际下载
  • gradle wrapper –gradle-version 5.2.1 –distribution-type all :关联源码用
  1. GradleWrapper执行流程
    • 1.当我们第一次执行 ./gradlew build 命令的时候,gradlew 会读取 gradle-wrapper.properties 文件的配置信息
    • 2.准确的将指定版本的 gradle 下载并解压到指定的位置(GRADLE_USER_HOME目录下的wrapper/dists目录中)
    • 3.并构建本地缓存(GRADLE_USER_HOME目录下的caches目录中),下载再使用相同版本的gradle就不用下载了
    • 4.之后执行的 ./gradlew 所有命令都是使用指定的 gradle 版本
    • gradle-wrapper.properties 说明
字段名称 说明
distributionBase 下载Gradle压缩包解压后存储的主目录
distributionPath 相对于distributionBase的解压后的gradle压缩包的路径
distributionUrl Gradle发行版压缩包的下载地址
zipStoreBase 同distributionBase,但是存放的是zip压缩包
zipStorePath 同distributionPath,存放的是zip压缩包

五、java插件

官网:https://docs.gradle.org/current/userguide/plugin_reference.html,以 Java 插件为例。
一些插件对工程目结构有约定,所以我们一般遵循它的约定结构来创建工程,这也是 Gradle 的“约定优于配置”原则

  1. 插件引入
    plugins {
    id 'java'
    }
  2. java 插件规定的项目源集目录
    
    └── src
    ├── main
    │   ├── java
    │   │   └── org
    │   │       └── lifefunker
    │   │           └── Main.java
    │   └── resources
    └── test
        ├── java
        └── resources
修改默认目录结构, 如下操作
```groovy
    sourceSets { 
    main {
        java {
            srcDirs = ['src/java']
    } 
    resources {
        srcDirs = ['src/resources'] }
    } 
   }
  1. java插件常见属性
属性名称 类型 默认值 描述
reportsDirName String reports 生成报告的目录名称
reportsDir File(只读) buildDir/reportsDirName 生成报告的目录
testResultsDirName String test-results 生成测试 result.xml 文件的目录名称
testResultsDir File(只读) reportsDir/testReportDirName 生成测试报告的目录
libsDirName String libs 生成 lib 库的目录名称
libsDir File(只读) buildDir/libsDirName 生成 lib 库的目录
distsDirName String distributions 生成发布文件的目录名称
distsDir File(只读) buildDir/distsDirName 生成发布文件的目录
docsDirName String docs 生成帮助文档的目录名称
docsDir File(只读) buildDi r/docsDirName 生成帮助文档的目录
dependencyCacheDirName String dependency-cache 存储缓存资源依赖信息的目录名称
dependencyCacheDir File(只读) buildDir/dependencyCacheDirName 存储缓存资源依赖信息的目录
sourceSets SourceSetContainer (只读) Not null 包含工程的资源集合(source sets.)
sourceCompatibility JavaVersion,也可以使用字符串或数字比如 ‘1.5’ 或者 1.5 根据使用的 JVM 定 编译 java 文件时指定使用的 java 版本
targetCompatibility JavaVersion,也可以使用字符串或数字比如 ‘1.5’ 或者 1.5 sourceCompatibility 生成 classes 的 java 版本
archivesBaseName String projectName 作为归档文件的默认名称,如 JAR 或者 ZIP 文件的名称
  1. 常见的依赖类型
类型名称 说明
compileOnly 由 java 插件提供,曾短暂的叫 provided,后续版本已经改成了 compileOnly,适用于编译期需要而不需要打包的情 况
runtimeOnly 由 java 插件提供,只在运行期有效,编译时不需要,比如 mysql 驱动包。,取代老版本中被移除的 runtime
implementation 由 java 插件提供,针对源码[src/main 目录] ,在编译、运行时都有效,取代老版本中被移除的 compile
testCompileOnly 由 java 插件提供,用于编译测试的依赖项,运行时不需要
testRuntimeOnly 由 java 插件提供,只在测试运行时需要,而不是在测试编译时需要,取代老版本中被移除的 testRuntime
testImplementation 由 java 插件提供,针对测试代码[src/test 目录] 取代老版本中被移除的 testCompile
providedCompile war 插件提供支持,编译、测试阶段代码需要依赖此类 jar 包,而运行阶段容器已经提供了相应的支持,所 以无需将这些文件打入到 war 包中了;例如 servlet-api.jar、jsp-api.jar
compile 编译范围依赖在所有的 classpath 中可用,同时它们也会被打包。在 gradle 7.0 已经移除
runtime runtime 依赖在运行和测试系统的时候需要,在编译的时候不需要,比如 mysql 驱动包。在 gradle 7.0 已经移除
api java-library 插件提供支持,这些依赖项可以传递性地导出给使用者,用于编译时和运行时。取代老版本中被 移除的 compile
compileOnlyApi java-library 插件提供支持,在声明模块和使用者在编译时需要的依赖项,但在运行时不需要。

官方文档参考:

六、Gretty插件

在 idea 新版本的创建项目中,无法自己选择创建项目是普通 java 工程还是 web 工程了【IDEA 旧版本是可以的】,所以我 们如果想创建 web 工程,只需要自己在 src/main/目录下添加 webapp/WEB-INF/web.xml 及页面即可。

  • 底层支持 jetty,tomcat 等 Servlet 容器
  • 支持项目热部署、HTTPS、调试
  • 效果等同于将项目打成 war 包,部署到本地 tomcat 运行

    1. 引入插件
      plugins {
      id ‘war’
      id 'org.gretty' version '2.2.0' 
      }

      引入插件后,刷新右边框的gradle插件,加载几分钟后插件中间就会有gretty插件的具体的任务列表

    2. 指定maven仓库
      repositories { 
      //指定jcenter仓库,一定要放在前面 
      jcenter()
      mavenCentral()
      }
    3. 设置gretty插件参数
      gretty {
      httpPort = 8888
      contextPath = "/web"
      debugPort = 5005
      debugSuspend = true
      httpsEnabled = true
      managedClassReload=true //修改了类之后重新加载
      //servletContainer = 'tomcat8' //如果不指定默认的servlet容器,支持tomcat7/8,默认是使用的是Jetty服务器 
      httpsPort = 4431
      }   
    4. 运行
      gradle appRun

      七、SpringBoot插件

    5. 引入插件
      plugins {
      id 'org.springframework.boot'version'2.3.7.RELEASE' //维护springboot版本号,不单独使用,和下面两个插件一起用 
      id 'io.spring.dependency-management' version '1.0.10.RELEASE' //进行依赖管理,在引入其它boot依赖时省略版本号、解决jar包冲突问题 
      id 'java'
      }
    6. 引入依赖
      dependencies {
      implementation 'org.springframework.boot:spring-boot-starter'
      implementation 'org.springframework.boot:spring-boot-starter-web' //省略版本,原生bom支持,插件management提供 
      testImplementation('org.springframework.boot:spring-boot-starter-test') 
      {
      exclude group: 'org.junit.vintage', module: 'junit-vintage-engine' 
      }
      }
      test { 
      useJUnitPlatform()
      }
    7. 执行命令

    要想运行当前 Springboot 项目,直接执行 gradle bootRun 指令或者 idea 右侧按钮即可。
    当然如果想让当前项目打成可执行 jar 包,只需执行: gradle bootJar 指令即可。
    Cloud 项目创建也可以借助于脚手架创建,与 Boot 项目类似。

  1. 拓展spring-boot-gradle-plugin 插件
    buildscript { 
    repositories {
        maven { url 'https://maven.aliyun.com/repository/public' }
    }
    dependencies {
        classpath 'org.springframework.boot:spring-boot-gradle-plugin:2.4.1'
    } 
    }
    apply plugin: 'org.springframework.boot'
    apply plugin: 'io.spring.dependency-management'

八、maven发布插件

  1. 引入插件
    plugins {
        id 'java-library' //如果发布war包,需要war插件,java-library支持带源码、文档发布 
        id 'maven-publish'
    }
  2. 设置发布代码
    //带源码和javadoc的发布:需要'java-library'插件支持:它是java的升级版,java插件的功能java-library都有 //javadoc.options.encoding="UTF-8"
    //java {
    // withJavadocJar()
    // withSourcesJar() 
    //}
    publishing {
    publications { 
        myLibrary(MavenPublication) {
        groupId = 'org.gradle.sample' //指定GAV坐标信息 
        artifactId = 'library'
        version = '1.1'
        from components.java//发布jar包
        //from components.web///引入war插件,发布war包 
        }
    }
    repositories {
        //本地仓库位于USER_HOME/.m2/repository 
        mavenLocal()
        //发布项目到私服中
        maven{
            name = 'myRepo' //name属性可选,表示仓库名称,url必填 //发布地址:可以是本地仓库或者maven私服
            //url = layout.buildDirectory.dir("repo")
            // change URLs to point to your repos, e.g. http://my.org/repo
            def releasesRepoUrl = layout.buildDirectory.dir('repos/releases')
            def snapshotsRepoUrl = layout.buildDirectory.dir('repos/snapshots')
            url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl
            credentials{
                username = 'user'
                password = 'secret'
            }
        }
    }
    }
  3. 执行命令

    执行发布命令,将项目发布到本地仓库或者远程仓库。常见的发布指令有:

    • generatePomFileForPubNamePublication: 生成 pom 文件
    • publishPubNamePublicationToRepoNameRepository:发布项目到指定仓库,如果没有仓库名,默认为 maven
    • publishPubNamePublicationToMavenLocal: 将 PubName 发布复制到本地 Maven 仓库中包括 POM 文件和其他元数据。
    • publish: 发布到 repositories 中指定的仓库(为比如 Maven 私服)
    • publishToMavenLocal: 执行所有发布任务中的操作发布到本地 maven 仓库【默认在用户家目录下的.m2/repository】。