大型Java项目中Gradle的应用
2014-04-29王毅敏
王毅敏
引言:Gradle是基于Groovy语言的构建工具。本文阐述了如何利用Gradle构建易描述的、可维护的、简洁的、高性能项目。
Gradle作为新的构建工具,它是基于Groovy语言的构建工具,既保持了Maven的优点,又通过使用Groovy定义的DSL[1],克服了Maven中使用XML繁冗以及不灵活等缺点。在最近这段时间里,我在使用Gradle作为构建脚本的大型Java项目上工作,更深切体会到Gradle在项目构建过程中是如此的简单、易用。
一、多Module的项目
Maven的一个缺点就是:Maven不支持多module的构建。在Micro-Service架构风格流行的今天,在一个项目里面包含多个Module已成为一种趋势。Gradle天然支持多module,并且提供了很多手段来简化构建脚本。在Gradle中,一个模块就是它的一个子项目(subproject),所以,我使用父项目来描述顶级项目,使用子项目来描述顶级项目下面的模块。
(一)配置子项目
在多模块的项目中,Gradle遵循惯例优于配置(Convention Over Configuration)原则。
在父项目的根目录下寻找settings.gradle文件,在该文件中设置想要包括到项目构建中的子项目。在构建的初始化阶段(Initialization),Gradle会根据settings.gradle文件来判断有哪些子项目被include到了构建中,并为每一个子项目初始化一个Project对象,在构建脚本中通过project(‘:sub-project-name)来引用子项目对应的Project对象。通常,多模块项目的目录结构要求将子模块放在父项目的根目录下,但是如果有特殊的目录结构,可以在settings.gradle文件中配置。
(二)共享配置
在大型Java项目中,子项目之间必然具有相同的配置项。我们在编写代码时,要追求代码重用和代码整洁;而在编写Gradle脚本时,同样需要保持代码重用和代码整洁。Gradle提供了不同的方式使不同的项目能够共享配置。
(三)独享配置
在项目中,除了设置共同配置之外,每个子项目还会有其独有的配置。比如每个子项目具有不同的依赖以及每个子项目特殊的task等。Gradle提供了两种方式来分别为每个子项目设置独有的配置(1)在父项目的build.gradle文件中通过project(‘:sub-project-name)来设置对应的子项目的配置。(2)我们还可以在每个子项目的目录里建立自己的构建脚本。对于子项目少,配置简单的小型项目,推荐使用第一种方式配置,这样就可以把所有的配置信息放在同一个build.gradle文件里。但是,若是对于子项目多,并且配置复杂的大型项目,使用第二种方式对项目进行配置会更好。因为,第二种配置方式将各个项目的配置分别放到单独的build.gradle文件中去,可以方便设置和管理每个子项目的配置信息。
(四)其他共享
在Gradle中,除了上面提到的配置信息共享,还可以共享方法以及Task。可以在根目录的build.gradle文件中添加所有子项目都需要的方法,在子项目的build.gradle文件中调用在父项目build.gradle脚本里定义的方法。
二、环境的配置
为了方便地将应用部署到开发、测试以及产品等不同环境上,Gradle提供了几种不同的方式为不同的环境打包,使得不同的环境可以使用不同的配置文件。此外,它还提供了简单的方法,使得我们能够便捷地初始化数据库。
(一)Properties配置
要为不同的环境提供不一样的配置信息,Maven选择使用profile,而Gradle则提供了两种方法为构建脚本提供Properties配置:第一种方式是使用传统的properties文件, 然后在使用Gradle时,通过传入不同的参数加载不同的properties文件。例如,我们可以在项目中提供development.properties、test.properties和production.properties。在项目运行时,使用-Pprofile=development来指定加载开发环境的配置。另外一种方式就是使用Groovy的语法,定义可读性更高的配置文件。
(二)替换
通过不同的方式加载不同环境的配置后,就需要把它们替换到有占位符的配置文件中去。在配置文件中使用@key@来标注要被替换的位置。
(三)初始化数据库
在项目开发过程中,为了方便为不同环境构建相同的数据库及数据,我们通常需创建数据库的表以及插入一些初始化数据。Gradle目前没有提供相关的Task或者Plugin,但是我们可以自己创建Task去运行SQL来初始化各个环境上的数据库。前面也提到Gradle是Groovy定义的DSL,所以我们可以在Gradle中使用Groovy的代码来执行SQL脚本文件。在Gradle脚本中,使用Groovy加载数据库的Driver之后,就可以使用Groovy提供的Sql类去执行SQL来初始化数据库了。
三、代码质量
代码质量是软件开发质量的一部分,除了人工代码评审之外,在把代码提交到代码库之前,还应该使用自动检查工具来自动检查代码,来保证项目的代码质量。下面介绍一下Gradle提供的支持代码检查的插件。
(一)CheckStyle
CheckStyle是SourceForge下的一个项目,提供了一个帮助JAVA开发人员遵守某些编码规范的工具。它能够自动化代码规范检查过程,从而使得开发人员从这项重要却枯燥的任务中解脱出来。
(二)FindBugs
FindBugs是一个静态分析工具,它检查类或者JAR文件,将字节码与一组缺陷模式进行对比以发现可能的问题。同样也可以在FindBugs的配置阶段(Configuration)设置其相关的属性,比如Report的输出目录、检查哪些sourceSet等。
(三)JDepend
在开发Java项目时经常会遇到关于包混乱的问题,JDepend工具可以帮助你在开发过程中随时跟踪每个包的依赖性(引用/被引用),从而设计高维护性的架构,不论是在打包发布还是版本升级都会更加轻松。在构建脚本中加入如下代码即可:apply plugin: 'jdepend'
(四)PMD
PMD是一种开源分析Java代码错误的工具。与其他分析工具不同的是,PMD通过静态分析获知代码错误,即在不运行Java程序的情况下报告错误。PMD附带了许多可以直接使用的规则,利用这些规则可以找出Java源程序的许多问题。
四、依赖
几乎每个Java项目都会用到开源框架。同时,对于具有多个子模块的项目来说,项目之间也会有所依赖。所以,管理项目中对开源框架和其他模块的依赖是每个项目必须面对的问题。同时,Gradle也使用Repository来管理依赖。
(一)Jar包依赖管理
Gradle沿用Maven的依赖管理方法,通过groupId、name和version到配置的Repository里寻找指定的Jar包。同样,它也提供了和Maven一样的构建生命周期,compile、runtime、testCompile和testRuntime分别对应项目不同阶段的依赖。
(二)子项目之间的依赖
对于多模块的项目,项目中的某些模块需要依赖于其他模块,前面提到在初始化阶段,Gradle为每个模块都创建了一个Project对象,并且可以通过模块的名字引用到该对象。在配置模块之间的依赖时,使用这种方式可以告诉Gradle当前模块依赖了哪些子模块。
(三)构建脚本的依赖
除了项目需要依赖之外,构建脚本本身也可以有自己的依赖。当使用一个非Gradle官方提供的插件时,就需要在构建脚本里指定其依赖,当然还需要指定该插件的Repository。在Gradle中,使用buildscript块为构建脚本配置依赖。
五、其他
(一)apply其他Gradle文件
当一个项目很复杂的时候,Gradle脚本也会很复杂,除了将子项目的配置移到对应项目的构建脚本之外,还可以按照不同的功能将复杂的构建脚本拆分成小的构建脚本,然后在build.gradle里使用apply from,将这些小的构建脚本引入到整体的构建脚本中去。
(二)project的目录
在脚本文件中,需要访问项目中的各级目录结构。Gradle为Project对象定义了一些属性指向项目的根目录,方便在脚本中引用:rootDir:在子项目的脚本文件中可以通过该属性访问到根项目路径。rootProject:在子项目中,可以通过该属性获取父项目的Project对象。
(三)使用Wrapper指定Gradle的版本
为了统一项目中Gradle的版本,可以在构建脚本中通过定义一个wrapper的Task,并在该Task中指定Gradle的版本以及存放Gradle的位置。
(四)使用gradle.properties文件
Gradle构建脚本会自动找同级目录下的gradle.properties文件,在这个文件中可以定义一些property,以供构建脚本使用。
结束语
由于篇幅有限,本文只是我在一个大型Java项目上使用Gradle的部分经验,并未涵盖所有Gradle相关的知识。另外,Gradle是基于Groovy的构建工具,在使用Gradle的时候也需要了解和使用Groovy。所以,在学习Gradle插件的过程中,也能学会Groovy相关的用法,可谓一举两得。
参考文献
[1]Wikipedia.Domain-specific language. http://en.wikipedia.org/wiki/Domain-specific_language.2013-11-8.
(作者单位:南京工业职业技术学院 国际教育学院)