当前位置: 首页 > 后端技术 > Java

微服务开发系列:为什么要用Gradle搭建

时间:2023-04-02 00:46:31 Java

源码地址微服务开发系列:开篇微服务开发系列:为什么选择Kotlin微服务开发系列:为什么要用Gradle搭建微服务开发系列:目录结构,保持文件环境干净开发系列:服务发现,Nacos的小补充微服务开发系列:框架中如何选择开源工具微服务开发系列:数据库orm使用微服务开发系列:如何打印日志微服务开发系列:鉴权微服务开发系列:认识连载的重要性微服务开发系列:设计统一的http接口内容形式微服务开发系列:使用异常特性将异常纳入框架管理微服务文档在这个微服务架构中,没有使用常见的maven作为管理工具,而是使用了gradle。在我使用maven构建这个架构并完成大部分工作后,我决定全面切换到gradle。我花了四天时间才完全熟悉并更换它。总结一下为什么在这个框架中使用gradle,以及使用gradle需要遵循的规则。1Maven和gradle相比起来思路是一样的,都是管理jar的依赖版本,定义一个项目从编译到打包的各个阶段。使用之后发现,对于小项目或者单体项目,其实maven和gradle没有什么区别,甚至小项目maven更方便,因为配置是预先定义好的,不需要做太多配置不多,直接Use就可以了,gradle想用它配置有点麻烦。但是对于多模块的项目,两层甚至三层的项目结构,使用gradle是绝对必要的。经常不能用maven实现的目的,在gradle中可以轻松实现。两者的根本区别在于,maven是基于配置的,配置设计取决于配置设计者是否留下修改后的界面,而gradle是基于脚本的,如何配置多半取决于用户如何设计。一个倡议是maven插件的作者,一个是gradle倡议的使用者。1.1mavenmaven的配置是固定的,不能修改。它只能根据配置进行定制。稍微超出配置的操作必须通过插件扩展来完成。比如我想在打包的时候使用gitsha1作为版本号,就必须安装一个git-commit-id-plugin插件。单层结构没什么问题,完全可以达到我使用的目的。两层结构勉强够用,三层结构好像没问题。但是当我想将一个项目作为依赖项提供给所有其他项目时,问题就来了\---server+---framework+---gateway\---business+---business-foundation+---business-webframework是我将作为基础依赖项提供给所有项目的东西。在这个需求中,maven有两个无法解决的问题:git-commit-id-plugin插件提供的变量git.commit.id.abbrev无法传递依赖,下面的方法无法实现,因为插件-in是处理依赖,编译打包的时候不生效,所以不能提供变量。cn.serverframework${git.commit.id.abbrev}maven无法解决项目间循环依赖,如果想让每个项目都不要手动引用framework,那我就得在顶层引用,但是framework也在这个framework里面,并且parent在顶层已经指定为server了。当然,如果parent不指定为server,也可以轻松解决这个问题,但是parent中的其他引用必须在framework中重新引用,并且不能设置framework的version变量。这两个问题困扰了我很久,直到我换成gradle。1.2gradle\---服务器:build.gradle.kts+---框架:build.gradle.kts+---网关:build.gradle.kts\---业务:build.gradle.kts+---business-foundation:build.gradle.kts+---business-web:build.gradle.ktsgradle使用脚本来管理项目,脚本的类型一般分为groovy和kotlin(kotlin用于架构)。它将编译、构建、打包、测试等阶段作为任务对象。您可以在脚本中编写代码来动态定义变量。例如,上面的第一个问题很容易解决。把代码直接写到.git文件夹下去搞定。defgetCheckedOutGitCommitHash(){defgitFolder="$projectDir/.git/"deftakeFromHash=12defhead=newFile(gitFolder+"HEAD").text.split(":")defisCommit=head.length==1if(isCommit)returnhead[0].trim().take(takeFromHash)defrefHead=newFile(gitFolder+head[1].trim())refHead.text.trim().taketakeFromHash}但是在这个框架中这里还是用com.palantir.git-version,因为专业插件考虑问题更全面。Gradle还可以定义每个阶段要做什么,你也可以在dependencies中写代码来决定哪些模块需要依赖什么。再比如上面的第二个问题,五行代码就可以解决。思路是判断模块不是framework就加依赖,不是framework就不加依赖,简单易懂。因为顶层配置在子项目中是继承的,所以不管是多少层结构,都可以使用。allprojects.forEach{project->if(project.name!="framework"){implementation(project(":framework"))}}2依赖版本指南无论使用maven还是gradle,子项目都不允许自己选择自己的dependencies的版本,必须有父项目或者顶层项目选择的版本。后面只讨论使用gradle的情况。顶层工程中定义的依赖分为两种依赖版本,由dependencyManagement决定。其实dependencies中的dependencies并不是直接引入的。这里定义的依赖是全局依赖,它不仅决定了版本,还提供了所有项目的依赖。子项目中使用的依赖不能盲目添加,必须遵循以下原则:在引入依赖之前,检查是否已经在需要的功能项目中提供了其他依赖,比如一系列的工具类,95%是已经包含在hutool-所有依赖项中;再比如,如果需要使用rpc功能,先研究一下,会发现redis客户端redisson已经搞定了,不需要再额外添加依赖;再比如分布式超时缓存,redisson也已经有了。你能想到的,优秀的开源项目都已经考虑过了。添加依赖项和依赖版本需要好好调查。该版本是否已被某些依赖项定义。如果使用spring系统中的官网依赖,大部分已经定义好了。比如spring-boot-starter-tomcat已经定义为spring-boot-dependencies提供,spring-cloud-dependencies和spring-cloud-alibaba-dependencies是同一个思路。这样做还可以防止依赖项之间的版本不一致。尽量减少依赖,及时清理无用的依赖,有用的依赖只在必要时才取。比如javacpp提供了很多基于Java的C库,涉及多个平台,只能使用需要的平台,不能一次性全部添加。打出来的版本大小需要几个G,非授权不得修改依赖版本。任何依赖升级应该只对项目经理来说,统一修改,一个或几个人应该找到合适的依赖升级方法。盲目升级只会破坏项目结构的稳定性。2.1修改上面提到的父依赖版本,添加依赖和依赖版本需要研究一下,版本是否已经被一些依赖定义了。但是,在项目中肯定会出现一些情况,需要升级一些依赖,而其他关联的依赖却没有升级。修复某些依赖项的漏洞是很常见的。因此,框架中也提供了修改方法。在server:build.gradle.kts中引入了插件io.spring.dependency-management,可以通过以下方式覆盖依赖版本的变量内容。ext["elasticsearch.version"]=elasticsearchVersion3项目打包在framework中,提供war、jar、bootJar三种打包方式。具体的行为模式在server:build.gradle.kts中定义。3.1提供warwar包用于运行tomcat或weblogic等项目。在tomcat下运行需要web.xml,在weblogic下运行需要weblogic.xml。如果打包时有这两个文件,则将其打包到包中。3.2jar这里的jar是为了方便war的更新。里面只打包了一个项目的类,没有额外的依赖库,直接替换war包解压出来的项目jar即可。这样更新起来就比较方便了。一个项目可能达到数百兆字节。如果只改代码,不修改依赖,只需要更新几十k个jar包就可以了。3.3bootJarbootJar是springboot打包打出来的。可以直接运行,使用更方便。当部署一个不复杂的系统时,简单地使用它。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")中定义。该命令使用:gradlebuildAll将所有模块的所有类型的包进行级联打包。gradlebusiness:buildAll级联打包business下所有模块的所有类型的包。gradlebusiness:business-web:buildAll指定要打包的项目。gradlebuildAll-Ppack=bootJar级联打包选择bootJar打包格式。gradlebuildAll-Ppack=bootJar,jar级联打包选择bootJar和jar打包格式。5Maven在很多地方可能只支持生产环境的maven。这种极端情况并不代表gradle不能用。框架中在项目根目录下添加了一个pom.xml,可以在执行maven包时自动调用gradlebuildAll。您还可以自定义要执行的其他命令。6使用gradle解决一些问题过程中的注意点。Gradle并不是一个特别好用的框架,从零开始掌握需要花费大量的时间,以及对开发本身有比较深入的了解。在我看来,是因为gradle同时支持groovy和kotlin构建脚本,而且gradle的版本变化太快。经常上网搜索gradle中某些需求的实现,比如打包时排除某些文件或包含文件,搜索结果五花八门。并不是说这些结果大部分都是无效的,而是很难判断一个方案是否符合你的要求,只能不断试错。许多解决方案要么无效,要么根本找不到。以处理子模块依赖时排除父模块依赖为例。在网上搜索了大量的解决方案。网上搜索的内容是gradleexcludeparentdependency。结果是有很多解决方案,但没有一个适合我。但我不知道问题的根源是因为我的gradle版本还是因为我正在使用的kotlin构建脚本。最终,我通过自己的摸索找到了解决办法。配置.implementation.get().exclude(group="org.springframework.session")