相比起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插件對代碼風格/格式作了靜態檢查。