简述Gradle在大型Java项目上的应用---(一)

在Java构建工具的世界里,先有了Ant,然后有了Maven。Maven的CoC[1]、依赖管理以及项目构建规则重用性等特点,让Maven几乎成为Java构建工具的事实标准。然而,冗余的依赖管理配置、复杂并且难以扩展的构建生命周期,都成为使用Maven的困扰。

Gradle作为新的构建工具,获得了2010 Springy大奖,并入围了2011的Jax最佳Java技术发明奖。它是基于Groovy语言的构建工具,既保持了Maven的优点,又通过使用Groovy定义的DSL[2],克服了 Maven中使用XML繁冗以及不灵活等缺点。在Eugene Dvorkin撰写的文章《最让人激动的5个Java项目》中,他是这样介绍Gradle的:

“工程自动化是软件项目成功的必要条件,而且它应该实现起来简单、易用、好玩。构建没有千篇一律的方法,所以Gradle没有死板的强加方法于我们,尽管你会认为查找和描述方法很重要,然而Gradle对于如何描述有着非常好的支持。我不认为工具能够拯救我们,但是Gradle能给你所需要的自由,你可以利用Gradle构建易描述的、可维护的、简洁的、高性能项目”。

在最近半年里,我在使用Gradle作为构建脚本的大型Java项目上工作,更深切体会到Gradle在项目构建过程中是如此的简单、易用。

1. 多Module的项目

Hibernate项目负责人Steve Ebersole在Hibernate将构建脚本从Maven换成Gradle时,专门写了一篇文章《Gradle: why?》,文中提到Maven的一个缺点就是:Maven不支持多module的构建。在Micro-Service[3]架构风格流行的今天,在一个项目里面包含多个Module已成为一种趋势。Gradle天然支持多module,并且提供了很多手段来简化构建脚本。在Gradle中,一个模块就是它的一个子项目(subproject),所以,我使用父项目来描述顶级项目,使用子项目来描述顶级项目下面的模块。

1.1 配置子项目

在多模块的项目中,Gradle遵循惯例优于配置 (Convention Over Configuration)原则。

在父项目的根目录下寻找settings.gradle文件,在该文件中设置想要包括到项目构建中的子项目。在构建的初始化阶段(Initialization),Gradle会根据settings.gradle 文件来判断有哪些子项目被include到了构建中,并为每一个子项目初始化一个Project对象,在构建脚本中通过project(‘:sub-project-name’)来引用子项目对应的Project对象。

通常,多模块项目的目录结构要求将子模块放在父项目的根目录下,但是如果有特殊的目录结构,可以在settings.gradle文件中配置。

我所在的项目包括:

一个描述核心业务的core模块

一个遗留的Enterprise Java Bean(enterprise-beans)模块

两个提供不同服务的Web项目(cis-war和admin-war)

一个通过schema生成jaxb对象的jaxb项目以及一个用来用来打ear包的ear项目

一个用于存放项目配置文件相关的config子目录。它不是子模块,所以 config不应该被加到项目的构建中去。

它们都放置在根项目目录下。我们通过如下的settings.gradle来设置项目中的子项目:

include 'core', 'enterprise-beans', 'cis-war', 'admin-war', 'jaxb', 'ear'

我们将需要加入到项目构建中的子项目配置在settings.gradle文件中,而没有加入不需要的config子目录。

1.2 共享配置

在大型Java项目中,子项目之间必然具有相同的配置项。我们在编写代码时,要追求代码重用和代码整洁;而在编写Gradle脚本时,同样需要保持代码重用和代码整洁。Gradle 提供了不同的方式使不同的项目能够共享配置。

allprojects:allprojects是父Project的一个属性,该属性会返回该Project对象以及其所有子项目。在父项目的build.gradle脚本里,可以通过给allprojects传一个包含配置信息的闭包,来配置所有项目(包括父项目)的共同设置。通常可以在这里配置IDE的插件,group和version等信息,比如:

allprojects {
    apply plugin: 'idea'
    }

这样就会给所有的项目(包括当前项目以及其子项目)应用上idea插件。

subprojects:subprojects和allprojects一样,也是父Project的一个属性,该属性会返回所有子项目。在父项目的build.gradle脚本里,给 subprojects传一个包含配置信息的闭包,可以配置所有子项目共有的设置,比如共同的插件、repositories、依赖版本以及依赖配置:

subprojects {
    apply plugin: 'java'
    repositories {
        mavenCentral()
    }
    ext {
          guavaVersion = ’14.0.1’
          junitVersion = ‘4.10’ 
   } 
 
    dependencies {
        compile(
                “com.google.guava:guava:${guavaVersion}”
        )
        testCompile(
                “junit:junit:${junitVersion}”
        )
    }
}

这就会给所有子项目设置上java的插件、使用mavenCentral作为 所有子项目的repository以及对Guava[4]和JUnit的项目依赖。此外,这里还在ext里配置依赖包的版本,方便以后升级依赖的版本。

configure:在项目中,并不是所有的子项目都会具有相同的配置,但是会有部分子项目具有相同的配置,比如在我所在的项目里除了cis-war和admin-war是web项目之外,其他子项目都不是。所以需要给这两个子项目添加war插件。Gradle的configure可以传入子项目数组,并为这些子项目设置相关配置。在我的项目中使用如下的配置:   

configure(subprojects.findAll {it.name.contains('war')}) {
    apply plugin: 'war'
    }

configure需要传入一个Project对象的数组,通过查找所有项目名包含war的子项目,并为其设置war插件。

1.3 独享配置

在项目中,除了设置共同配置之外, 每个子项目还会有其独有的配置。比如每个子项目具有不同的依赖以及每个子项目特殊的task等。Gradle提供了两种方式来分别为每个子项目设置独有的配置。

在父项目的build.gradle文件中通过project(‘:sub-project-name’)来设置对应的子项目的配置。比如在子项目core需要Hibernate的依赖,可以在父项目的build.gradle文件中添加如下的配置:   

project(‘:core’) {
      ext{
                   hibernateVersion = ‘4.2.1.Final’
      }
	dependencies { 
    		compile “org.hibernate:hibernate-core:${hibernateVersion}”
}
}

注意这里子项目名字前面有一个冒号(:)。 通过这种方式,指定对应的子项目,并对其进行配置。

我们还可以在每个子项目的目录里建立自己的构建脚本。在上例中,可以在子项目core目录下为其建立一个build.gradle文件,并在该构建脚本中配置core子项目所需的所有配置。例如,在该build.gradle文件中添加如下配置:   

 ext{
       hibernateVersion = ‘4.2.1.Final’
      }
	dependencies { 
    	compile “org.hibernate:hibernate-core:${hibernateVersion}”
}

根据我对Gradle的使用经验,对于子项目少,配置简单的小型项目,推荐使用第一种方式配置,这样就可以把所有的配置信息放在同一个build.gradle文件里。例如我同事郑晔的开源项目moco。它只有两个子项目,因而就使用了第一种方式配置,在项目根目录下的build.gradle文件中设置项目相关的配置信息。但是,若是对于子项目多,并且配置复杂的大型项目,使用第二种方式对项目进行配置会更好。因为,第二种配置方式将各个项目的配置分别放到单独的build.gradle文件中去,可以方便设置和管理每个子项目的配置信息。

1.4 其他共享

在Gradle中,除了上面提到的配置信息共享,还可以共享方法以及Task。可以在根目录的build.gradle文件中添加所有子项目都需要的方法,在子项目的build.gradle文件中调用在父项目build.gradle脚本里定义的方法。例如我定义了这样一个方法,它可以从命令行中获取属性,若没有提供该属性,则使用默认值:

def defaultProperty(propertyName, defaultValue) {
    return hasProperty(propertyName) ? project[propertyName] : defaultValue
}

注意,这段脚本完全就是一段Groovy代码,具有非常好的可读性。

由于在父项目中定义了defaultProperty方法,因而在子项目的build.gradle文件中,也可以调用该方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值