关于gradle:微服务开发系列为什么用-gradle-构建

51次阅读

共计 5111 个字符,预计需要花费 13 分钟才能阅读完成。

构建

在该微服务架构中,并没有应用常见的 maven 作为管理工具,而是应用了 gradle。

我在应用 maven 搭建这个架构实现了大部分的工作之后,决定全面转向 gradle,花了四天的工夫才全面相熟与替换。

总结一下这个我的项目中为什么应用 gradle,以及应用 gradle 须要恪守的规定。

1 maven 与 gradle

两者相比拟思维是一样的,都是治理 jar 的依赖版本,定义了一个我的项目从编译到打包两头的各个阶段。

在应用下来之后发现,对于小型或者单个我的项目,应用 maven 与 gradle 实际上没有什么差异,甚至 小型我的项目 maven 更加不便一些,因为配置都是提前定义好的,不须要做过多的配置,就能间接应用,而 gradle 想要用起来配置略微麻烦一些。

然而 对于多模块我的项目,两层甚至三层我的项目构造时,应用 gradle 相对是必须的,应用 maven 经常不能达到的目标,在 gradle 外面轻松就可能实现。

两者基本的区别是,maven 是配置型的,配置的设计依赖于配置的设计者是否留有批改的接口,gradle 是脚本型的,如何配置绝大部分取决于使用者如何设计。

一个主动权在 maven 插件作者,一个主动权在 gradle 的使用者。

1.1 maven

maven 的配置是固定的,不可批改的,只能基于配置上做定制化的革新,略微超出配置之外的操作,就要通过插件扩大来实现。

比如说我心愿打包的时候将 git sha1 作为版本号,你必须要装置一个 git-commit-id-plugin 插件。

在单层构造下没什么,齐全能够达到我应用的目标,两层构造也勉强够用,三层构造貌似也没什么问题。

然而当我应用心愿把一个我的项目提供给其它所有我的项目作为依赖时,问题就来了

 \--- server
    +---framework
    +---gateway
    \---business
        +---business-foundation
        +---business-web

framework 就是我将要提供给所有我的项目作为根本依赖。在这个需要外面,maven 有两个问题解决不了:

  1. git-commit-id-plugin 插件提供的变量 git.commit.id.abbrev 没法传递 dependency,像上面的形式,就无奈实现,因为插件在解决依赖时是不失效的,只有在编译打包的时候能力失效,因而变量也就无奈提供。

     <dependency>
         <groupId>cn.server</groupId>
         <artifactId>framework</artifactId>
         <version>${git.commit.id.abbrev}</version>
     </dependency>
  2. maven 无奈解决我的项目之间的循环依赖,如果心愿各个我的项目不必本人手动援用 framework,那么我就要在顶层去援用,然而 framework 也在这个框架之中,parent 曾经被指定顶层我的项目是 server,当然不指定 parent 是 server,可能很轻易的解决这个问题,然而在 parent 中的其它援用,都要在 framework 中被从新援用一遍,并且还不能设置 framework 的版本变量。

这两个问题困扰了我十分久,直到应用 gradle 替换了之后。

1.2 gradle

 \--- server:build.gradle.kts
    +---framework:build.gradle.kts
    +---gateway:build.gradle.kts
    \---business:build.gradle.kts
        +---business-foundation:build.gradle.kts
        +---business-web:build.gradle.kts

gradle 是应用脚本去治理我的项目的,脚本的类型个别分为 groovy 与 kotlin(架构中应用的是 kotlin)。

它把编译、构建、打包、测试等阶段,都看做 task 对象,你能够在脚本中写代码动静的定义变量,比方上述第一个问题,解决起来非常简单,间接写代码去 .git 文件夹上来获取。

def getCheckedOutGitCommitHash() {
    def gitFolder = "$projectDir/.git/"
    def takeFromHash = 12
    def head = new File(gitFolder + "HEAD").text.split(":")
    def isCommit = head.length == 1

    if(isCommit) return head[0].trim().take(takeFromHash)

    def refHead = new File(gitFolder + head[1].trim())
    refHead.text.trim().take takeFromHash}

然而在这个框架里,还是应用了 com.palantir.git-version,因为业余的插件思考问题更加全面。

gradle 还能够定义每个阶段去做什么,你还能够在依赖中去写代码,去判断什么模块须要依赖什么。

再比方上述第二个问题,应用五行代码就能够解决,思路就是当我判断模块不是 framework 就增加依赖,不是就不增加,简略易懂。

因为顶层的配置在 subprojects 中都是继承的,不论是几层的构造,都可能应用。

allprojects.forEach { project ->
    if (project.name != "framework") {implementation(project(":framework"))
    }
}

2 依赖版本准则

不论是应用 maven 还是 gradle,子项目都不容许本人抉择依赖的版本,必须有上一级我的项目或者顶级项目选择版本。前面只探讨应用 gradle 的状况。

在顶层我的项目中定义的依赖分两种

  1. dependencyManagement 确定的依赖版本,实际上不间接引入依赖
  2. dependencies 中依赖,在这里定义的依赖,即为全局依赖,既确定了版本,又给所有我的项目提供了依赖应用

子项目中应用依赖,不可自觉的增加,要准守上面的准则:

  1. 引入依赖之前,查看所需性能我的项目中是否曾经有其它依赖提供,比方一系列的工具类,95% 都曾经蕴含在 hutool-all 的依赖中;再比方,如果你须要应用 rpc 性能,先调研,你会发现 redis 客户端 redisson 曾经做到了,不须要再增加额定的依赖;再比方分布式超时缓存,redisson 同样曾经有了。你能想到的货色,优良的开源我的项目早就曾经思考到了。
  2. 增加依赖以及依赖版本须要做好调研,版本是否曾经被某些依赖定义,如果你是应用 spring 体系中的官网依赖,那么大部分曾经定义好了,比方 spring-boot-starter-tomcat 就曾经被 spring-boot-dependencies 提供了,spring-cloud-dependencies 和 spring-cloud-alibaba-dependencies 都是同样的思路。这样做的同时也能避免依赖之间的版本不统一问题
  3. 依赖要应用最小化的准则,无用的依赖及时清理,有用的依赖留神只取必须的,举个例子,javacpp 提供了很多 Java 中应用 C 的库,波及到多个平台,只取须要的平台应用,不能一股脑的都增加进来,这样打进去的版本要几个 G 的大小
  4. 不容许本人擅自批改依赖的版本,任何依赖的降级,都应该只会我的项目管理者,做对立批改,应该由某个或者某些人,去找到一种适合的依赖降级形式,自觉的降级,只会毁坏我的项目构造的稳固

2.1 批改父级依赖版本

下面提到过,增加依赖以及依赖版本须要做好调研,版本是否曾经被某些依赖定义。

然而我的项目中必定有须要降级某些依赖,但不降级其它关联依赖的状况,常见于修复某些依赖的破绽。

于是,框架中也提供了批改形式。

server:build.gradle.kts 中,引入了插件 io.spring.dependency-management,它可能让你用上面的形式,笼罩依赖版本的变量内容。

ext["elasticsearch.version"] = elasticsearchVersion

3 我的项目打包

在框架中,提供了三种打包形式 warjarbootJar

具体的行为模式,都定义在 server:build.gradle.kts 中。

3.1 war

war 包是提供给 tomcat 或者 weblogic 或者其它我的项目运行应用的。

在 tomcat 下运行须要应用 web.xml,weblogic 下运行须要 weblogic.xml,打包时如果有这两个文件,都会打到包外面。

3.2 jar

这里的 jar 是为了不便 war 的更新,外面只有一个我的项目的 classes 打包,没有任何额定的依赖库,能够间接替换 war 包中解压进去的我的项目 jar。

这样更新起来就较为不便,一个我的项目可能达到上百兆,只改代码不批改依赖的状况下,只须要更新几十 k 的 jar 包即可。

3.3 bootJar

bootJar 就是 spring boot 自带的打包打进去的,能够间接运行,应用起来比拟不便,在部署并不简单的零碎时候,简略应用一下。

3.4 模块自主抉择打包格局

框架外面提供了自主抉择打包格局的配置,如果有非凡的须要,也可能在外面做自定义的配置,比方拷贝非凡的文件等,具体怎么应用的参考顶层 server:build.gradle.kts 外面中的例子。

tasks {
    bootJar {enabled = true}

    jar {enabled = true}

    "war"(War::class) {enabled = true}
}

如果是一个父节点,不须要参加构建打包,指定即可。

build {enabled = false}

4 打包形式

应用命令 gradle 或者 我的项目外面带的 gradlew 可执行命令。

gradlew 简略解释一下就是为了防止不同的 gradle 版本差别过大导出呈现问题,相当于把我的项目的 gradle 版本固定了。

多说一嘴就是 gradle 进化太快,从 1.x 进化到 6.x,很多中央不兼容,maven 同样也有这个问题,然而当初 maven3 曾经是支流,并且应用形式非常固定,所以较少遇见过非凡状况。

框架中提供了 buildAll 这个指令,可能执行三种形式打包,办法定义在 server:build.gradle.kts > subprojects > tasks> register(name = "buildAll") 中。

命令应用:
gradle buildAll

级联打包所有模块的所有类型包。

gradle business:buildAll

级联打包 business 下所有模块的所有类型包。

gradle business:business-web:buildAll

指定我的项目去打包。

gradle buildAll -Ppack=bootJar

级联打包抉择 bootJar 打包格局。

gradle buildAll -Ppack=bootJar,jar

级联打包抉择 bootJar 和 jar 打包格局。

5 在 Maven 中应用

很多中央生产环境有可能呈现只反对 maven 的状况,这种极其状况也不代表 gradle 就没法应用了。

框架中在我的项目根目录外面减少了一个 pom.xml,可能在执行 maven package 的状况下,主动调用 gradle buildAll

也能够自定义其它命令去执行。

6 留神点

从我应用 gradle 解决一些问题的过程来看。

gradle 并不是一个特地容易应用的框架,从头驾驭它须要大量的工夫,以及对开发自身理解的要绝对深刻。

从我的角度来看,是因为 gradle 同时反对了 groovy 和 kotlin 构建脚本的形式,以及 gradle 的版本变动太快。

我常常在网上搜寻一些 gradle 中某些需要的实现形式,比方打包时排除某些或蕴含文件,其搜寻后果形形色色。

并不是说这些后果大部分都是有效的,而是很难判断一种解决方案是否合乎本人的要求,只能一直试错。

很多解决方案要么是有效的,要么基本找不到对应的办法。

拿解决子模块依赖时排除来自父模块的依赖为例,我查找了网上大量的解决方案,在网上搜寻内容是 gradle exclude parent dependency,后果是解决方案很多,然而没有一种是我可能应用的,但我不分明问题的起源是因为我的 gradle 版本问题,还是因为我应用的是 kotlin 构建脚本的问题。

最终还是通过本人的摸索找到了解决方案。

configurations.implementation.get().exclude(group = "org.springframework.session")

本文参加了思否技术征文,欢送正在浏览的你也退出。

正文完
 0