相比起Maven的XML配置方式,Gradle提供了一套简明的DSL用于构建Java项目,使咱们就像编写程序同样编写项目构建脚本。本文将从无到有建立一个用Gradle构建的Spring Boot项目,并在此过程当中讲到Gradle的一些典型用法。html
本文Github代码:https://github.com/davenkin/gradle-spring-boot.gitjava
Gradle采用了与Maven相同的目录组织结构,你能够经过Spring Initializr网站建立Spring Boot工程。可是在本文中,咱们将所有经过命令行操做建立Spring Boot工程。首先在命令行中建立以下目录结构:git
└── src ├── main │ └── java └── test └── java
而后在src同级目录中添加一个build.gradle文件,内容以下:github
apply plugin: 'java'
大功告成,一个用Gradle构建的Java项目建立好了,尽情用如下命令编译并打包我们的Java项目吧:web
gradle build
只是如今我们的Java项目仍是一个空架子,不用急,在下文中咱们将一步一步在这个空架子中搭建一个有血有肉的Spring Boot项目。spring
值得一提的是,虽然此时的build.gradle文件中只有一行配置(apply plugin: 'java',做用是引入java插件),可是其背后已经帮咱们作了不少事情,好比它使得咱们可以运行gradle build命令。这里的build即为Gradle中的一个任务(Task),咱们还能够运行如下命令查看到更多的Task。api
gradle tasks
此时输出:浏览器
... Build tasks ----------- assemble - Assembles the outputs of this project. build - Assembles and tests this project. buildDependents - Assembles and tests this project and all projects that depend on it. buildNeeded - Assembles and tests this project and all projects it depends on. classes - Assembles main classes. clean - Deletes the build directory. jar - Assembles a jar archive containing the main classes. testClasses - Assembles test classes. ...
这里的assemble、build和jar等Task都是java插件引入的。build.gradle是Gradle的配置文件,更多关于Gradle的知识请参考笔者的Gradle学习系列文章。服务器
对于全部的Gradle项目来讲,笔者都推荐使用Gradle Wrapper,甚至应该将其当作建立代码库以后的第一件事来作。使用Gradle Wrapper有如下好处:app
在build.gradle中加入如下配置:
task wrapper(type: Wrapper) { gradleVersion = '3.0' }
而后在命令行运行:
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,依然能够经过如下命令进行项目构建:
./gradlew build
若是你的项目有持续集成(CI)服务器(你也应该有),那么你的CI机器也没有必要安装Gradle了。另外,此时全部人都是使用的相同版本的gradle,进而避免了因为版本不一样所带来的问题。
在本文中,咱们的业务很是简单———输出“Hello World!”。可是麻雀虽小,五脏俱全,首先须要在build.gradle中配置spring-boot插件,并引入Spring的Web组件,整个build.gradle以下:
buildscript { repositories { jcenter() } dependencies { classpath("org.springframework.boot:spring-boot-gradle-plugin:1.5.2.RELEASE") } } repositories { jcenter() } apply plugin: 'java' apply plugin: 'org.springframework.boot' sourceCompatibility = 1.8 targetCompatibility = 1.8 task wrapper(type: Wrapper) { gradleVersion = '3.0' } dependencies { compile("org.springframework.boot:spring-boot-starter-web") testCompile("org.springframework.boot:spring-boot-starter-test") }
而后建立Application类:
@SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
依然很简单,是吧?!这个Application类即是Spring Boot程序的入口。另外咱们还须要一个Controller和一个业务类HelloWorld:
HelloWorldController:
@RestController("/helloworld") public class HelloController { private HelloWorld helloWorld; public HelloController(HelloWorld helloWorld) { this.helloWorld = helloWorld; } @GetMapping public String hello() { return helloWorld.hello(); } }
HelloWorld:
@Component public class HelloWorld { public String hello() { return "Hello World!"; } }
此时工程的目录结构为:
├── README.md ├── build.gradle ├── gradle │ └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── src ├── main │ └── java │ └── davenkin │ ├── Application.java │ ├── HelloController.java │ └── HelloWorld.java └── test └── java
而后运行:
./gradlew bootRun
在浏览器或者Postman中打开http://localhost:8080/gradle-spring-boot/helloworld,即可以看到久违的"Hello World!"了。
我曾经看到很多人在Eclipse或者IntelliJ IDEA中导入Maven/Gradle工程,甚至在IDE中使用嵌入Tomcat容器。我并不推荐这么作,这些严重依赖于GUI操做的功能实际上是很笨拙、很脆弱的。以嵌入Tomcat容器为例,它要求项目中全部人都在本身的IDE中手动地对Tomcat进行配置,而手动的过程老是容易出错的。在持续交付中有个原则是“凡是可以自动化的,都应该自动化”,这里的自动化说白了其实就是代码化。
所以,在使用Gradle时,笔者更推崇的一种方式是经过Gradle的IDE插件一键式地生成IDE工程文件,而后在IDE中直接打开这样的工程文件。这样的好处一是很是简单,二是全部人都使用了相同的IDE配置。
在Gradle中配置IntelliJ IDEA插件,只需在build.gradle中配置:
apply plugin: 'idea'
而后运行:
./gradlew idea
此时将生成后缀为ipr的IntelliJ IDEA工程文件,在IntelliJ IDEA中直接打开(Open)该文件便可。
对于Eclipse,在build.gradle中增长如下配置:
apply plugin: 'eclipse'
而后运行:
./gradlew eclipse
此时将生成Eclipse的.project工程文件。
请注意,全部IDE工程文件都不该该提交到代码库,对于Git来讲应该将这些文件注册到.gitignore文件中。各个开发者拿到代码后须要各自运行./graldlw idea或./gradlew eclipse命令以生成本地工程文件。
至少有两种方式能够对Spring Boot项目进行调试。一种是直接运行命令:
./gradlew bootRun --debug-jvm
此时程序将默认监听5005端口,并暂停以等待调试客户端的链接,而后启动Spring Boot。
另外一种方式是使用Gradle的Application插件,在build.gradle中添加:
apply plugin: 'application' applicationDefaultJvmArgs = [ "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005" ]
此时运行:
./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进行改造。
咱们的目的是:
能够看到,我么将Gradle默认的测试设施用于了单元测试,也即对于单元测试咱们不须要作任何改变。对于API测试而言,首先咱们须要添加名为apiTest的源代码集合(SrouceSet),该SourceSet即对应了src/apiTest/java目录,在build.gradle文件中增长以下配置:
sourceSets { apiTest { compileClasspath += main.output + test.output runtimeClasspath += main.output + test.output } } configurations { apiTestCompile.extendsFrom testCompile apiTestRuntime.extendsFrom testRuntime }
而后,添加一个Test类型的Task用于运行src/apiTest/java目录下的API测试代码:
task apiTest(type: Test) { testClassesDir = sourceSets.apiTest.output.classesDir classpath = sourceSets.apiTest.runtimeClasspath }
为了使Intelli IDEA可以感知到这些新添加的测试代码,咱们须要对Gradle的idea插件进行额外配置:
idea { module { testSourceDirs += file('src/apiTest/java') testSourceDirs += file('src/apiTest/resources') scopes.TEST.plus += [configurations.apiTestCompile] scopes.TEST.plus += [configurations.apiTestRuntime] } }
另外,为了使本地构建(./gradlew biuld)过程可以先运行单元测试,再运行API测试,咱们还须要作如下配置:
apiTest.mustRunAfter test build.dependsOn apiTest
第一行的意思是API测试必须运行在单元测试以后,第二行的意思是将API测试包含在build任务中。
JaCoCo是一款代码测试覆盖率统计工具,咱们主要将其用于统计单元测试的覆盖率。在build.gradle中增长配置:
apply plugin: "jacoco"
此时运行./gradlew build以后,JaCoCo将在build/jacoco目录下为单元测试和API测试分别生成原始数据文件(test.exec和apiTest.exec),可是此时并无测试报告生成,为此,咱们还须要单独运行:
./gradlew jacocoTestReport
在浏览器中打开build/report/jacoco/test/index.html,你将看到单元测试覆盖率报告:
可是,此时的覆盖率报告只是针对单元测试的,为了获得API测试的覆盖率,咱们须要添加一个新的Task:
task jacocoApiTestReport(type: JacocoReport){ sourceSets sourceSets.main executionData apiTest }
而后运行:
./gradlew jacocoApiTestReport
在浏览器中打开build/report/jacoco/jacocoApiTestReport/index.html,你将看到单元测试覆盖率报告。
有时,咱们但愿看到单元测试和API测试的总体覆盖率,此时咱们须要再添加一个Task:
//Unit Test and API Test Code coverage all together task jacocoAllTestReport(type: JacocoReport){ sourceSets sourceSets.main executionData test, apiTest }
而后运行:
./gradlew jacocoAllTestReport
在浏览器中打开build/report/jacoco/jacocoAllTestReport/index.html,你将看到全部测试整合后的覆盖率报告。
做为演示,咱们在HelloWorld中添加一个新的anotherHello()方法,此时HelloWorld为:
@Component public class HelloWorld { public String hello() { return "Hello World!"; } public String anotherHello() { return "Another Hello World!"; } }
对应的HelloWorldController也变为:
@RestController public class HelloController { private HelloWorld helloWorld; public HelloController(HelloWorld helloWorld) { this.helloWorld = helloWorld; } @GetMapping("/helloworld") public String hello() { return helloWorld.hello(); } @GetMapping("/anotherHelloworld") public String anotherHello() { return helloWorld.anotherHello(); } }
而后,咱们让HelloWorld的单元测试只测试hello()方法,让API测试只测试anotherHello()方法(也即只调用“anotherHelloworld”的URL接口)。
此时单元测试覆盖率为:
能够看到,anohterHello()方法没有被单元测试覆盖到。而集成测试虽然覆盖到了anotherHello()方法,却没有覆盖到hello()方法:
整体测试覆盖率为:
此时,整体测试覆盖率同时统计了单元测试和集成测试的覆盖率。
CheckStyle是一种静态代码检查工具,主要用于检查代码风格或格式是否知足要求。首先,咱们须要一份配置文件来配置这样的要求,这里咱们采用Google的Checkstyle配置文件。
在biuld.gradle中增长checkstyle插件:
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插件对代码风格/格式作了静态检查。