项目或工作中存在的问题
- JAVA项目开发和打包要求的环境和配置不一致,因此程序员误将开发配置上传的SVN或者GIT上之后,会替换了原生成环境配置,导致生产、测试时安装失败。不利于持续集成。通过gradle配置可以有效解决这个问题。
- 不同的项目需要相同的打包步骤,导致编写构建代码重复,很可以引起配置不一致,导致持续集成配置复杂。通过gradle插件开发,标准化构建,方便使用,统一配管配置。
- 项目构建后要求有固定的格式,gradle构建可以解决这个问题。
背景技术及术语解释
Gradle 构建已经相当的普遍,目前使用的spring框架已使用gradle构建。
gradle 是用groovy(Groovy是一种基于JVM的动态语言)编写工具。可以帮助团队构建,持续集成,快速交付的工具,是一种DSL,即Domain Specific Language,领域相关语言,有自己特有的术语。
Gradle特点:
构建文件脚本化。可编程化,构建文件冗余少,可维护性更高。
自动缓存文件信息,重新编译必要的部分,加快编译速度。
可编程。定义自定义任务,满足项目定义的需要。
可与其他构建工具集成(ant,maven)
自动的版本管理库
丰富的插件
免费使用,使用apache license 2 协议。项目代码在https://github.com/gradle/gradle
-
学习成本低,非深入定制,不需要了解内部实现机制,只要会写配置文件即可。
gradle和ant、maven的关系和比较
Ant:Ant是用java编写的.构建文件采用xml。 Ant的功能虽然强大,但过于灵活,规范性不足,对目录结构及build.xml没有默认约定,且没有统一的项目依赖管理(项目依赖需要和apache Ivy结合使用)。
Maven:Maven解决了规范性的问题,依赖统一管理的问题,统一了构建了生命周期,可插件扩展功能。Maven的理念是约定大于配置,但由于规范性太强(包括生命周期),灵活性不足,pom.xml采用Xml结构,项目一大,Xml就显得冗长。
Gradle:综合了Ant和Maven的优点,吸收了Ant中task的思想,然后把Maven的目录规范以及仓库思想也融合了进来,但允许用户自由的修改默认的规范(如,可随意修改源码目录),配置文件则采用Groovy语言来书写,Groovy是一门可编程语言,配置文件本身就可以视为一份源代码,并最终交由Gradle来处理执行。
本方案技术详细描述
案例1:
案例说明: 项目源代码(如图1所示)下有个tools文件夹,tools文件夹下有1个项目使用说明和项目启动脚本,现需要将项目打包成图2的形式,即将项目打包成jar包,并将tools下的内容构建于jar文件同一目录下,方便项目启动;并且要将src/main/resources/下的application.yml打包的时候复制到config目录下;并且为了传输方便,还得将整个项目打包成zip包。目前maven除了自己开发插件实现外,无法实现该功能,但是通过gradle可以轻松的实现。
方式一:在项目根目录的build.gradle文件内加入如下代码。
`task copyReadme(type: Copy){`
`from ('tools')`
`into libsDir`
`}`
`task copyResources(type: Copy){`
`from ('src/main/resources/application.yml')`
`into "$libsDir/config/"`
`}`
`task unzip(type: Copy) {`
`println "==============================>unzip"`
`into "$buildDir/classes/java/main"`
`}`
`task zip(type: Zip, dependsOn: build){`
`into ('event-simulation'){`
`from libsDir`
`}`
`}`
`build.finalizedBy(copyReadme,copyResources)`
代码解释及原理说明:gradle构建有个接口是finalizedBy,作用是在于在构建结束时调用自定义的任务。
所以我们自定义2个task,task的格式是闭包。
闭包在groovy中是一种类型,类似与java中的lambda表达式和接口,具体请查阅groovy语言。
第一个task是复制脚本,任务的类型是复制type: Copy
, 从tools文件夹复制到编译目录下的lib文件夹下。
第二个task是将application.yml文件从src/main/resources复制到编译目录下的lib文件夹下的config目录下。如果没有该目录会自动创建。
然后我们使用finalizedBy
去调用他们。
最后我们创建第三个task,我们命名为zip,任务类型是Zip,dependsOn
是依赖于构建,内容是将lib文件夹下的内容打包成event-simulation.zip。
那么我们如何运行呢?在项目根目录下我们运行
`gradle clean zip`
因为我们声明zip依赖与build,所以先执行build编译项目,在项目编译结束时调用上述2个方法,最后调用我们的zip方法打包。
但这种方法还是有点繁琐,在这里列举希望大家有个基本的概念,我们来看第二种实现方式。我们在第二种实现中加入新的功能:将项目的名称和版本号到我们的说明文档中。
方式二:
`apply plugin: 'distribution'`
`distributions {`
`main {`
`archivesBaseName = baseName`
`contents {`
`from (libsDir,'tools')`
`rename (archivesBaseName + '-' + version, archivesBaseName)`
`into ('config') {`
`from ('src/main/resources/application.yml')`
`}`
`eachFile { file ->`
`if (file.name.startsWith('readme')) {`
`filter{ line -> line.contains('${version}') ? line.replace('${version}', version) : line}`
`}`
`}`
`}`
`}`
`}`
方式二的目标是一样的,我们使用gradle内置的插件完成,插件的作用是在编译输出文件夹下创建distributions文件夹,并将编译后的内容进行打包成zip文件。
首先,声明使用插件apply plugin: 'distribution'
;
然后,我们使用插件的固定格式来定义,步骤如下:
第一步,声明变量archivesBaseName,即压缩包的名称为项目名称;
第二步,复制libsDir下的文件(编译好的jar包)和tools下的文件(脚本和readme)复制到根目录;
第三步,重命名archivesBaseName-version为压缩包名称;
第四步,将application.yml文件从src/main/resources/目录下复制到根目录的config文件夹下;
第五步,打包时过滤文件,将readme文件中含义变量${version}
替换为版本目前项目中的版本号。
定义好打包内容后,最后我们运行:
gradle clean distributions
最终得到图三的zip包,这样就可以发给第三方使用了。
案例2:
本案例的目标是因为开发文件和生成环境文件经常产生冲突,导致生成环境打包内容不符合生成环境的要求。具体如下。
原spring-boot项目在开发中使用application-dev.yml的配置文件,是否启用该文件的通过application.yml的
`spring:`
`prifiles:`
`active: dev`
来控制,当application.yml有变动时,经常会误将改配置变更导致失败,所以我们从技术手段上防止该事情的方式。
首先,我们创建application-prod.yml作为生产环境的配置文件;
然后,我们在生产环境构建时,自动的去修改这里的配置为prod,这样可以保证每次构建都是启用application-prod.yml这个文件,
接着,由于日志文件在开发可生产环境中配置也不一致,我们一并修改。
最后,我们在构建时将开发配置文件排除在生产环境的包外。
我们在项目根目录的build.gradle加入如下代码。
ext.PROD = 'prod'
processResources {
filteringCharset = 'UTF-8'
String buildType = project.getProperties().get('buildType')
if (buildType == PROD){
eachFile {file ->
if(file.name == 'application.yml') {
filter { line -> line.contains('active') ? line.replace('dev','prod') : line }
}
if(file.name == 'logback-spring.xml') {
filter { line -> line.contains('logs') ? line.replace('logs','${catalina.base}/logs') : line }
}
}
exclude 'application-dev.yml'
}
}
首先,我们新建一个变量,变量名称是PROD,当构建时,如果改变量名称为prod时,按生产环境的方式构建。
然后,我们声明查询的字符集是UTF-8;
接着,从命令行获取buildType参数,当此参数等于prod时,执行生产环境构建,
第一步,在过滤文件时获取名称为application.yml,并修改配置,
第二步,在过滤文件时获取名称为logback-spring.xml的日志配置文件,替换其中的变量,
第三步,排除application-dev.yml
最后,我们在生产环境的自动化构建时,执行如下命令
1. `gradle clean build -x test -P buildType=prod`
到此我们将生成环境和开发环境进行了彻底的分割,再也不用担心误传导致的构建失败了。