相比起Maven的XML配置方式,Gradle提供了一套简明的DSL用于构建Java项目,使我们就像编写程序一样编写项目构建脚本。本文将从无到有创建一个用Gradle构建的Spring Boot项目,并在此过程中讲到Gradle的一些典型用法。

本文Github代码:https://github.com/davenkin/gradle-spring-boot.git


创建Gradle工程

Gradle采用了与Maven相同的目录组织结构,你可以通过Spring Initializr网站创建Spring Boot工程。但是在本文中,我们将全部通过命令行操作创建Spring Boot工程。首先在命令行中创建如下目录结构:

  1. └── src
  2. ├── main
  3.    └── java
  4. └── test
  5. └── java

然后在src同级目录中添加一个build.gradle文件,内容如下:

  1. apply plugin: 'java'

大功告成,一个用Gradle构建的Java项目创建好了,尽情用以下命令编译并打包咱们的Java项目吧:

  1. gradle build

只是现在咱们的Java项目还是一个空架子,不用急,在下文中我们将一步一步在这个空架子中搭建一个有血有肉的Spring Boot项目。

值得一提的是,虽然此时的build.gradle文件中只有一行配置(apply plugin: 'java',作用是引入java插件),但是其背后已经帮我们做了很多事情,比如它使得我们能够运行gradle build命令。这里的build即为Gradle中的一个任务(Task),我们还可以运行以下命令查看到更多的Task。

  1. gradle tasks

此时输出:

  1. ...
  2. Build tasks
  3. -----------
  4. assemble - Assembles the outputs of this project.
  5. build - Assembles and tests this project.
  6. buildDependents - Assembles and tests this project and all projects that depend on it.
  7. buildNeeded - Assembles and tests this project and all projects it depends on.
  8. classes - Assembles main classes.
  9. clean - Deletes the build directory.
  10. jar - Assembles a jar archive containing the main classes.
  11. testClasses - Assembles test classes.
  12. ...

这里的assemble、build和jar等Task都是java插件引入的。build.gradle是Gradle的配置文件,更多关于Gradle的知识请参考笔者的Gradle学习系列文章


使用Gradle Wrapper

对于所有的Gradle项目来说,笔者都推荐使用Gradle Wrapper,甚至应该将其当做创建代码库之后的第一件事来做。使用Gradle Wrapper有以下好处:

  1. 不用安装gradle也能运行gradle
  2. 所有人使用相同的gradle版本

在build.gradle中加入以下配置:

  1. task wrapper(type: Wrapper) {
  2. gradleVersion = '3.0'
  3. }

然后在命令行运行:

  1. gradle wrapper

此时会生成以下三个文件(夹):gradlew、gradlew.bat和gradle目录。

这里的gradlew和gradlew.bat其实只是脚本文件(前者用于Unix/Linux/Mac,后者用于Windows),在使用gradle命令的地方替换为gradlew或gradlew.bat,他们将自动下载指定的gradle版本,然后用该版本进行项目构建。如上文所示,本文中我们配置gradle版本为3.0。

请注意,这三个文件(夹)都需要提交到代码库中。当项目其他人拿到代码之后,由于gradlew和gradlew.bat文件均在源代码中,他们本地即便没有gradle,依然可以通过以下命令进行项目构建:

  1. ./gradlew build

如果你的项目有持续集成(CI)服务器(你也应该有),那么你的CI机器也没有必要安装Gradle了。另外,此时所有人都是使用的相同版本的gradle,进而避免了由于版本不同所带来的问题。


添加Spring Boot依赖

在本文中,我们的业务非常简单———输出“Hello World!”。但是麻雀虽小,五脏俱全,首先需要在build.gradle中配置spring-boot插件,并引入Spring的Web组件,整个build.gradle如下:

  1. buildscript {
  2. repositories {
  3. jcenter()
  4. }
  5. dependencies {
  6. classpath("org.springframework.boot:spring-boot-gradle-plugin:1.5.2.RELEASE")
  7. }
  8. }
  9. repositories {
  10. jcenter()
  11. }
  12. apply plugin: 'java'
  13. apply plugin: 'org.springframework.boot'
  14. sourceCompatibility = 1.8
  15. targetCompatibility = 1.8
  16. task wrapper(type: Wrapper) {
  17. gradleVersion = '3.0'
  18. }
  19. dependencies {
  20. compile("org.springframework.boot:spring-boot-starter-web")
  21. testCompile("org.springframework.boot:spring-boot-starter-test")
  22. }

然后创建Application类:

  1. @SpringBootApplication
  2. public class Application {
  3. public static void main(String[] args) {
  4. SpringApplication.run(Application.class, args);
  5. }
  6. }

依然很简单,是吧?!这个Application类便是Spring Boot程序的入口。另外我们还需要一个Controller和一个业务类HelloWorld:

HelloWorldController:

  1. @RestController("/helloworld")
  2. public class HelloController {
  3. private HelloWorld helloWorld;
  4. public HelloController(HelloWorld helloWorld) {
  5. this.helloWorld = helloWorld;
  6. }
  7. @GetMapping
  8. public String hello() {
  9. return helloWorld.hello();
  10. }
  11. }

HelloWorld:

  1. @Component
  2. public class HelloWorld {
  3. public String hello() {
  4. return "Hello World!";
  5. }
  6. }

此时工程的目录结构为:

  1. ├── README.md
  2. ├── build.gradle
  3. ├── gradle
  4.    └── wrapper
  5.    ├── gradle-wrapper.jar
  6.    └── gradle-wrapper.properties
  7. ├── gradlew
  8. ├── gradlew.bat
  9. └── src
  10. ├── main
  11.    └── java
  12.    └── davenkin
  13.    ├── Application.java
  14.    ├── HelloController.java
  15.    └── HelloWorld.java
  16. └── test
  17. └── java

然后运行:

  1. ./gradlew bootRun

在浏览器或者Postman中打开http://localhost:8080/gradle-spring-boot/helloworld,便可以看到久违的"Hello World!"了。


生成IDE工程文件

我曾经看到不少人在Eclipse或者IntelliJ IDEA中导入Maven/Gradle工程,甚至在IDE中使用嵌入Tomcat容器。我并不推荐这么做,这些严重依赖于GUI操作的功能其实是很笨拙、很脆弱的。以嵌入Tomcat容器为例,它要求项目中所有人都在自己的IDE中手动地对Tomcat进行配置,而手动的过程总是容易出错的。在持续交付中有个原则是“凡是能够自动化的,都应该自动化”,这里的自动化说白了其实就是代码化。

因此,在使用Gradle时,笔者更推崇的一种方式是通过Gradle的IDE插件一键式地生成IDE工程文件,然后在IDE中直接打开这样的工程文件。这样的好处一是非常简单,二是所有人都使用了相同的IDE配置。

在Gradle中配置IntelliJ IDEA插件,只需在build.gradle中配置:

  1. apply plugin: 'idea'

然后运行:

  1. ./gradlew idea

此时将生成后缀为ipr的IntelliJ IDEA工程文件,在IntelliJ IDEA中直接打开(Open)该文件即可。

对于Eclipse,在build.gradle中增加以下配置:

  1. apply plugin: 'eclipse'

然后运行:

  1. ./gradlew eclipse

此时将生成Eclipse的.project工程文件。

请注意,所有IDE工程文件都不应该提交到代码库,对于Git来说应该将这些文件注册到.gitignore文件中。各个开发者拿到代码后需要各自运行./graldlw idea或./gradlew eclipse命令以生成本地工程文件。


调试

至少有两种方式可以对Spring Boot项目进行调试。一种是直接运行命令:

  1. ./gradlew bootRun --debug-jvm

此时程序将默认监听5005端口,并暂停以等待调试客户端的连接,然后启动Spring Boot。

另一种方式是使用Gradle的Application插件,在build.gradle中添加:

  1. apply plugin: 'application'
  2. applicationDefaultJvmArgs = [ "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005" ]

此时运行:

  1. ./gradlew bootRun

程序将启动并监听5005调试端口,但是与第一种方法不同的是,程序不会暂停,而是将直接启动整个Spring Boot程序。如果你想调试Spring Boot在启动过程中的某些代码,比如Spring框架启动代码,那么请选择第一种方式;否则,第二种是更合适的选择,因为我们并不是每次启动程序都一定会调试的,对吧?!


自动化测试

软件项目可以包含多种自动化测试,比如单元测试、集成测试、功能测试等。对于Spring Boot项目来说,笔者推荐将自动化测试划分为单元测试和API测试,其中单元测试即是传统的单元测试,而API测试包含了集成测试、功能测试和端到端测试的功能,它的测试对象是程序向外暴露的REST API接口,在测试时我们需要启动整个Spring Boot程序,然后模拟客户端调用这些API接口来完成业务测试用例。

单元测试相对比较简单,Spring Boot也提供了一些有助于单元测试的设施,但是我并不推荐大家使用,因为单元测试应该是非常纯粹、粒度非常小的测试,不应该有框架掺和。

通常来说,单元测试和API测试应该是分离的,也即他们的代码应该是分开的,运行测试的命令也应该是不同的。但是这给Gradle带来了难题,因为默认情况下Gradle只提供一个./gradlew test命令用于测试,并且默认要求测试代码位于src/test/java目录下。为此,我们需要对Gradle进行改造。

我们的目的是:

  • 默认的src/test/java目录用于单元测试代码,通过./gradlew test运行
  • 新建src/apiTest/java目录用于API测试代码,通过./gradlew apiTest运行

可以看到,我么将Gradle默认的测试设施用于了单元测试,也即对于单元测试我们不需要做任何改变。对于API测试而言,首先我们需要添加名为apiTest的源代码集合(SrouceSet),该SourceSet即对应了src/apiTest/java目录,在build.gradle文件中增加如下配置:

  1. sourceSets {
  2. apiTest {
  3. compileClasspath += main.output + test.output
  4. runtimeClasspath += main.output + test.output
  5. }
  6. }
  7. configurations {
  8. apiTestCompile.extendsFrom testCompile
  9. apiTestRuntime.extendsFrom testRuntime
  10. }

然后,添加一个Test类型的Task用于运行src/apiTest/java目录下的API测试代码:

  1. task apiTest(type: Test) {
  2. testClassesDir = sourceSets.apiTest.output.classesDir
  3. classpath = sourceSets.apiTest.runtimeClasspath
  4. }

为了使Intelli IDEA能够感知到这些新添加的测试代码,我们需要对Gradle的idea插件进行额外配置:

  1. idea {
  2. module {
  3. testSourceDirs += file('src/apiTest/java')
  4. testSourceDirs += file('src/apiTest/resources')
  5. scopes.TEST.plus += [configurations.apiTestCompile]
  6. scopes.TEST.plus += [configurations.apiTestRuntime]
  7. }
  8. }

另外,为了使本地构建(./gradlew biuld)过程能够先运行单元测试,再运行API测试,我们还需要做以下配置:

  1. apiTest.mustRunAfter test
  2. build.dependsOn apiTest

第一行的意思是API测试必须运行在单元测试之后,第二行的意思是将API测试包含在build任务中。


使用JaCoCo

JaCoCo是一款代码测试覆盖率统计工具,我们主要将其用于统计单元测试的覆盖率。在build.gradle中增加配置:

  1. apply plugin: "jacoco"

此时运行./gradlew build之后,JaCoCo将在build/jacoco目录下为单元测试和API测试分别生成原始数据文件(test.exec和apiTest.exec),但是此时并没有测试报告生成,为此,我们还需要单独运行:

  1. ./gradlew jacocoTestReport

在浏览器中打开build/report/jacoco/test/index.html,你将看到单元测试覆盖率报告:

但是,此时的覆盖率报告只是针对单元测试的,为了得到API测试的覆盖率,我们需要添加一个新的Task:

  1. task jacocoApiTestReport(type: JacocoReport){
  2. sourceSets sourceSets.main
  3. executionData apiTest
  4. }

然后运行:

  1. ./gradlew jacocoApiTestReport

在浏览器中打开build/report/jacoco/jacocoApiTestReport/index.html,你将看到单元测试覆盖率报告。

有时,我们希望看到单元测试和API测试的整体覆盖率,此时我们需要再添加一个Task:

  1. //Unit Test and API Test Code coverage all together
  2. task jacocoAllTestReport(type: JacocoReport){
  3. sourceSets sourceSets.main
  4. executionData test, apiTest
  5. }

然后运行:

  1. ./gradlew jacocoAllTestReport

在浏览器中打开build/report/jacoco/jacocoAllTestReport/index.html,你将看到所有测试整合后的覆盖率报告。

作为演示,我们在HelloWorld中添加一个新的anotherHello()方法,此时HelloWorld为:

  1. @Component
  2. public class HelloWorld {
  3. public String hello() {
  4. return "Hello World!";
  5. }
  6. public String anotherHello() {
  7. return "Another Hello World!";
  8. }
  9. }

对应的HelloWorldController也变为:

  1. @RestController
  2. public class HelloController {
  3. private HelloWorld helloWorld;
  4. public HelloController(HelloWorld helloWorld) {
  5. this.helloWorld = helloWorld;
  6. }
  7. @GetMapping("/helloworld")
  8. public String hello() {
  9. return helloWorld.hello();
  10. }
  11. @GetMapping("/anotherHelloworld")
  12. public String anotherHello() {
  13. return helloWorld.anotherHello();
  14. }
  15. }

然后,我们让HelloWorld的单元测试只测试hello()方法,让API测试只测试anotherHello()方法(也即只调用“anotherHelloworld”的URL接口)。

此时单元测试覆盖率为:

可以看到,anohterHello()方法没有被单元测试覆盖到。而集成测试虽然覆盖到了anotherHello()方法,却没有覆盖到hello()方法:

总体测试覆盖率为:

此时,总体测试覆盖率同时统计了单元测试和集成测试的覆盖率。


使用Checkstyle

CheckStyle是一种静态代码检查工具,主要用于检查代码风格或格式是否满足要求。首先,我们需要一份配置文件来配置这样的要求,这里我们采用Google的Checkstyle配置文件。

在biuld.gradle中增加checkstyle插件:

  1. apply plugin: 'checkstyle'

下载Google的checkstyle文件并将其拷贝为config/checkstyle/checkstyle.xml,Gradle的checkstyle插件默认将读取该配置文件。CheckStyle检查将包含在./gradlew build中。注:在笔者电脑上,使用Google原始Checkstyle配置文件总是报错,对Checkstyle进行了一些精简之后运行成功。


总结

在本文中,我们从无到有创建了一个使用Gradle构建的Spring Boot项目,包括了对项目的编译打包、运行单元测试和API测试,并且获得测试覆盖率报告。另外,我们提倡使用Gradle的idea/eclipse插件生成IDE工程文件,最后我们使用Checkstyle插件对代码风格/格式做了静态检查。

用Gradle构建Spring Boot项目的更多相关文章

  1. 用 gradle 运行 spring boot 项目

    用 gradle 运行 spring boot 项目(网页中的第6章:https://docs.spring.io/spring-boot/docs/2.1.1.RELEASE/gradle-plug ...

  2. spring boot系列01--快速构建spring boot项目

    最近的项目用spring boot 框架 借此学习了一下 这里做一下总结记录 非常便利的一个框架 它的优缺点我就不在这背书了 想了解的可以自行度娘谷歌 说一下要写什么吧 其实还真不是很清楚,只是想记录 ...

  3. 基于Gradle的spring boot 项目构建

    今天听只是分享,听到不用maven而使用Gradle构建,就尝试了下 Java三大构建工具:Ant.Maven和Gradle Gradle是一个基于Apache Ant和Apache Maven概念的 ...

  4. 使用IDEA构建Spring Boot项目简单实例

    一.介绍 它的目标是简化Spring应用和服务的创建.开发与部署,简化了配置文件,使用嵌入式web服务器,含有诸多开箱即用的微服务功能,可以和spring cloud联合部署. Spring Boot ...

  5. docker 构建 spring boot项目

    在docker 开始部署springBoot项目 1.在centos7 ~ 创建一个文件夹docker 里面放置 上面的Dockerfile 和 springBoot 打包的项目docker_spri ...

  6. 笔记:Spring Boot 项目构建与解析

    构建 Maven 项目 通过官方的 Spring Initializr 工具来产生基础项目,访问 http://start.spring.io/ ,如下图所示,该页面提供了以Maven构建Spring ...

  7. Spring Boot - 项目构建与解析

    构建 Maven 项目 通过官方的 Spring Initializr 工具来产生基础项目,访问 http://start.spring.io/ ,如下图所示,该页面提供了以Maven构建Spring ...

  8. 创建Spring Boot项目的几种方式总结

    一.我们可以使用Spring Initializr来创建SpringBoot项目. Spring Initializr从本质上来说就是一个Web应用程序,它能为你生成Spring Boot项目结构.虽 ...

  9. 使用Docker部署Spring boot项目

    Docker是一个使用广泛的Linux容器管理工具包,它允许用户创建镜像,并将其容器实例化.通过本指南,我们可以学习到如何使用Docker部署Spring Boot项目. 先决条件 开发之前,你必须具 ...

随机推荐

  1. Javascript学习一

    //学习moocjs1 JavaScript-警告(alert 消息对话框) <script type="text/javascript"> var mynum = 3 ...

  2. 使用Nuget管理dll

    前言 nuget 已经不是什么新东西,它是vs的一个扩展工具,可以让我们在项目中添加.删除.更新引用变得更加快捷方便.现在有许多传统公司对dll的管理还是很落后的,有些甚至时通过发送dll文件,这样做 ...

  3. iOS runtime的应用实例

      一直想弄明白runtime是怎么回事,因为面试的时候这是一道必备问题,但是平时用的机会真的少之又少,我一度以为runtime只是用来装13的利器,没什么卵用.但是随着学习的增多,发现runtime ...

  4. [Hadoop] - Protocol Buffer安装

    Hadoop从2.x版本开始,底层的RPC远程调用使用ProtocolBuffer格式来传递数据,所以在编译Hadoop的过程中有可能出现提示缺少Protocol服务的异常信息,类似:'protoc ...

  5. jQuery给CheckBox全选与不全选

    $(function(){ $("#checkAll").click(function() {//全选 $('input[name="DATA"]').prop ...

  6. Linux SVN安装部署

    系统:centos6.3 svn: subversion-1.6.1 apache: httpd-2.2.29 //创建svn路径 [root@localhost /]# mkdir svn [roo ...

  7. VisualStudio2017下ASP.NET CORE的TagHelper智能提示解决办法

    之前在VS2017RC中就发现该问题,安装了依赖,但是前段一直点不出来asp-for,后来查了发行说明, 才知道在VS2017rc中暂时无法解决,所以一直等到VS2017正式版的发布,急冲冲的装好, ...

  8. ThinkPhp模板转Flask模板

    Template Converter 网上的PHP资源很多,项目要用Python,所以想起做一个模板转换器,从ThinkPhp转成Flask的Jinja模板. 直接指定两个目录,将目录下的模板文件转换 ...

  9. 1787: [Ahoi2008]Meet 紧急集合

    1787: [Ahoi2008]Meet 紧急集合 Time Limit: 20 Sec  Memory Limit: 162 MBSubmit: 1482  Solved: 652[Submit][ ...

  10. 算法模板——Tarjan强连通分量

    功能:输入一个N个点,M条单向边的有向图,求出此图全部的强连通分量 原理:tarjan算法(百度百科传送门),大致思想是时间戳与最近可追溯点 这个玩意不仅仅是求强连通分量那么简单,而且对于一个有环的有 ...