你正在閱讀本書,說明你是一位讀書人。也許你是一個書蟲,博覽群書;也許你只讀自己需要的東西,拿起本書只是為了知道怎麼用Spring開發應用程序。
無論何種情況,你都是一位讀書人,是讀書人便有心維護一個閱讀列表,裡面是自己想讀或者需要讀的書。就算沒有白紙黑字的列表,至少在你心裡會有這麼一個列表。1
1如果你不是一個讀書人,就把書換成想看的電影、想去的餐廳,只要合適自己就好。
在本書中,我們會構建一個簡單的閱讀列表應用程序。在這個程序裡,用戶可以輸入想讀的圖書信息,查看列表,刪除已經讀過的書。我們將使用Spring Boot來輔助快速開發,各種繁文縟節越少越好。
開始前,我們需要先初始化一個項目。在第1章裡,我們看到了好幾種從Spring Initializr開始Spring Boot開發的方法。因為選擇哪種方法都行,所以要選個最合適的,著手用Spring Boot開發就好了。
從技術角度來看,我們要用Spring MVC來處理Web請求,用Thymeleaf來定義Web視圖,用Spring Data JPA來把閱讀列表持久化到數據庫裡,姑且先用嵌入式的H2數據庫。雖然也可以用Groovy,但是我們還是先用Java來開發這個應用程序吧。此外,我們使用Gradle作為構建工具。
無論是用Web界面、Spring Tool Suite還是IntelliJ IDEA,只要用了Initializr,你就要確保勾選了Web、Thymeleaf和JPA這幾個復選框。還要記得勾上H2復選框,這樣才能在開發應用程序時使用這個內嵌式數據庫。
至於項目元信息,就隨便你寫了。以閱讀列表為例,我創建項目時使用了圖2-1中的信息。
圖 2-1 通過Initializr的Web界面初始化閱讀列表應用程序
如果你創建項目時用的是Spring Tool Suite或者IntelliJ IDEA,那麼把圖2-1的內容適配成IDE需要的東西就好了。
另一方面,如果用Spring Boot CLI來初始化應用程序,可以在命令行裡鍵入以下內容:
$ spring init -dweb,data-jpa,h2,thymeleaf --build gradle readinglist
請記住,CLI的init
命令是不能指定項目根包名和項目名的。包名默認是demo,項目名默認是Demo。在項目創建完畢之後,你可以打開項目,把包名demo改為readinglist,把DemoApplication.java改名為ReadingListApplication.java。
項目創建完畢後,你應該能看到一個類似圖2-2的項目結構。
圖 2-2 初始化後的readinglist項目結構
這個項目結構基本上和第1章裡Initializr生成的結構是一樣的,只不過你現在真的要去開發應用程序了,所以讓我們先放慢腳步,仔細看看初始化的項目裡都有什麼東西。
2.1.1 查看初始化的Spring Boot新項目
圖2-2中值得注意的第一件事是,整個項目結構遵循傳統Maven或Gradle項目的佈局,即主要應用程序代碼位於src/main/java目錄裡,資源都在src/main/resources目錄裡,測試代碼則在src/test/java目錄裡。此刻還沒有測試資源,但如果有的話,要放在src/test/resources裡。
再進一步,你會看到項目裡還有不少文件。
build.gradle:Gradle構建說明文件。
ReadingListApplication.java
:應用程序的啟動引導類(bootstrap class),也是主要的Spring配置類。application.properties
:用於配置應用程序和Spring Boot的屬性。ReadingListApplicationTests.java
:一個基本的集成測試類。
因為構建說明文件裡有很多Spring Boot的優點尚未揭秘,所以我打算把最好的留到最後,先讓我們來看看ReadingListApplication.java
。
1. 啟動引導Spring
ReadingListApplication
在Spring Boot應用程序裡有兩個作用:配置和啟動引導。首先,這是主要的Spring配置類。雖然Spring Boot的自動配置免除了很多Spring配置,但你還需要進行少量配置來啟用自動配置。正如代碼清單2-1所示,這裡只有一行配置代碼。
代碼清單2-1
ReadingListApplication.java
不僅是啟動引導類,還是配置類
package readinglist;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication ←---開啟組件掃瞄和自動配置
public class ReadingListApplication {
public static void main(String args) {
SpringApplication.run(ReadingListApplication.class, args); ←---負責啟動引導應用程序
}
}
@SpringBootApplication
開啟了Spring的組件掃瞄和Spring Boot的自動配置功能。實際上,@SpringBootApplication
將三個有用的註解組合在了一起。
Spring的
@Configuration
:標明該類使用Spring基於Java的配置。雖然本書不會寫太多配置,但我們會更傾向於使用基於Java而不是XML的配置。Spring的
@ComponentScan
:啟用組件掃瞄,這樣你寫的Web控制器類和其他組件才能被自動發現並註冊為Spring應用程序上下文裡的Bean。本章稍後會寫一個簡單的Spring MVC控制器,使用@Controller
進行註解,這樣組件掃瞄才能找到它。Spring Boot的
@EnableAutoConfiguration
:這個不起眼的小註解也可以稱為@Abracadabra
2,就是這一行配置開啟了Spring Boot自動配置的魔力,讓你不用再寫成篇的配置了。
2abracadabra的意思是咒語。——譯者注
在Spring Boot的早期版本中,你需要在ReadingListApplication
類上同時標上這三個註解,但從Spring Boot 1.2.0開始,有@SpringBootApplication
就行了。
如我所說,ReadingListApplication
還是一個啟動引導類。要運行Spring Boot應用程序有幾種方式,其中包含傳統的WAR文件部署。但這裡的main
方法讓你可以在命令行裡把該應用程序當作一個可執行JAR文件來運行。這裡向SpringApplication.run
傳遞了一個ReadingListApplication
類的引用,還有命令行參數,通過這些東西啟動應用程序。
實際上,就算一行代碼也沒寫,此時你仍然可以構建應用程序嘗嘗鮮。要構建並運行應用程序,最簡單的方法就是用Gradle的bootRun
任務:
$ gradle bootRun
bootRun
任務來自Spring Boot的Gradle插件,我們會在2.1.2節裡詳細討論。此外,你也可以用Gradle構建項目,然後在命令行裡用java
來運行它:
$ gradle build
...
$ java -jar build/libs/readinglist-0.0.1-SNAPSHOT.jar
應用程序應該能正常運行,啟動一個監聽8080端口的Tomcat服務器。要是願意,你可以用瀏覽器訪問http://localhost:8080,但由於還沒寫控制器類,你只會收到一個HTTP 404(NOT FOUND)錯誤,看到錯誤頁面。在本章結束前,這個URL將會提供一個閱讀列表應用程序。
你幾乎不需要修改ReadingListApplication.java
。如果你的應用程序需要Spring Boot自動配置以外的其他Spring配置,一般來說,最好把它寫到一個單獨的@Configuration
標注的類裡。(組件掃瞄會發現並使用這些類的。)極度簡單的情況下,可以把自定義配置加入ReadingListApplication.java
。
2. 測試Spring Boot應用程序
Initializr還提供了一個測試類的骨架,可以基於它為你的應用程序編寫測試。但ReadingListApplicationTests
(代碼清單2-2)不止是個用於測試的佔位符,它還是一個例子,告訴你如何為Spring Boot應用程序編寫測試。
代碼清單2-2
@SpringApplicationConfiguration
加載Spring應用程序上下文
package readinglist;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import readinglist.ReadingListApplication;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(
classes = ReadingListApplication.class) ←---通過Spring Boot加載上下文
@WebAppConfiguration
public class ReadingListApplicationTests {
@Test
public void contextLoads { ←---測試加載的上下文
}
}
一個典型的Spring集成測試會用@ContextConfiguration
註解標識如何加載Spring的應用程序上下文。但是,為了充分發揮Spring Boot的魔力,這裡應該用@SpringApplicationConfiguration
註解。正如你在代碼清單2-2里看到的那樣,ReadingListApplicationTests
使用@SpringApplicationConfiguration
註解從ReadingListApplication
配置類裡加載Spring應用程序上下文。
ReadingListApplicationTests
裡還有一個簡單的測試方法,即contextLoads
。實際上它就是個空方法。但這個空方法足以證明應用程序上下文的加載沒有問題。如果ReadingListApplication
裡定義的配置是好的,就能通過測試。如果有問題,測試就會失敗。
當然,現在這只是一個新的應用程序,你還會添加自己的測試。但contextLoads
方法是個良好的開端,此刻可以驗證應用程序提供的各種功能。第4章會更詳細地討論如何測試Spring Boot應用程序。
3. 配置應用程序屬性
Initializr為你生成的application.properties文件是一個空文件。實際上,這個文件完全是可選的,你大可以刪掉它,這不會對應用程序有任何影響,但留著也沒什麼問題。
稍後,我們肯定有機會向application.properties裡添加幾個條目。但現在,如果你想小試牛刀,可以加一行看看:
server.port=8000
加上這一行,嵌入式Tomcat的監聽端口就變成了8000,而不是默認的8080。你可以重新運行應用程序,看看是不是這樣。
這說明application.properties文件可以很方便地幫你細粒度地調整Spring Boot的自動配置。你還可以用它來指定應用程序代碼所需的配置項。在第3章裡我們會看到好幾個例子,演示application.properties
的這兩種用法。
要注意的是,你完全不用告訴Spring Boot為你加載application.properties
,只要它存在就會被加載,Spring和應用程序代碼都能獲取其中的屬性。
我們差不多已經把初始化的項目介紹完了,還剩最後一樣東西,讓我們來看看Spring Boot應用程序是如何構建的。
2.1.2 Spring Boot項目構建過程解析
Spring Boot應用程序的大部分內容都與其他Spring應用程序沒有什麼區別,與其他Java應用程序也沒什麼兩樣,因此構建一個Spring Boot應用程序和構建其他Java應用程序的過程類似。你可以選擇Gradle或Maven作為構建工具,描述構建說明文件的方法和描述非Spring Boot應用程序的方法相似。但是,Spring Boot在構建過程中耍了些小把戲,在此需要做個小小的說明。
Spring Boot為Gradle和Maven提供了構建插件,以便輔助構建Spring Boot項目。代碼清單2-3是Initializr創建的build.gradle文件,其中應用了Spring Boot的Gradle插件。
代碼清單2-3 使用Spring Boot的Gradle插件
buildscript {
ext {
springBootVersion = `1.3.0.RELEASE`
}
repositories {
mavenCentral
}
dependencies {
classpath(\"org.springframework.boot:spring-boot-gradle-plugin: ${springBootVersion}\") ←---依賴Spring Boot插件
}
}
apply plugin: \'java\'
apply plugin: \'eclipse\'
apply plugin: \'idea\'
apply plugin: \'spring-boot\' ←---運用Spring Boot插件
jar {
baseName = \'readinglist\'
version = \'0.0.1-SNAPSHOT\'
}
sourceCompatibility = 1.7
targetCompatibility = 1.7
repositories {
mavenCentral
}
dependencies {
compile(\"org.springframework.boot:spring-boot-starter-web\") ←---起步依賴
compile(\"org.springframework.boot:spring-boot-starter-data-jpa\")
compile(\"org.springframework.boot:spring-boot-starter-thymeleaf\")
runtime(\"com.h2database:h2\")
testCompile(\"org.springframework.boot:spring-boot-starter-test\")
}
eclipse {
classpath {
containers.remove(\'org.eclipse.jdt.launching.JRE_CONTAINER\')
containers \'org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7\'
}
}
task wrapper(type: Wrapper) {
gradleVersion = \'1.12\'
}
另一方面,要是選擇用Maven來構建應用程序,Initializr會替你生成一個pom.xml文件,其中使用了Spring Boot的Maven插件,如代碼清單2-4所示。
代碼清單2-4 使用Spring Boot的Maven插件及父起步依賴
<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<project xmlns=\"http://maven.apache.org/POM/4.0.0\"
xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"
xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd\">
<modelVersion>4.0.0</modelVersion>
<groupId>com.manning</groupId>
<artifactId>readinglist</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>ReadingList</name>
<description>Reading List Demo</description>
<parent> ←---從spring-boot-starterparent繼承版本號
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>{springBootVersion}</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies> ←---起步依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<properties>
<project.build.sourceEncoding>
UTF-8
</project.build.sourceEncoding>
<start-class>readinglist.Application</start-class>
<java.version>1.7</java.version>
</properties>
<build>
<plugins>
<plugin> ←---運用Spring Boot插件
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
無論你選擇Gradle還是Maven,Spring Boot的構建插件都對構建過程有所幫助。你已經看到過如何用Gradle的bootRun
任務來運行應用程序了。Spring Boot的Maven插件與之類似,提供了一個spring-boot:run
目標,如果你使用Maven,它能實現相同的功能。
構建插件的主要功能是把項目打包成一個可執行的超級JAR(uber-JAR),包括把應用程序的所有依賴打入JAR文件內,並為JAR添加一個描述文件,其中的內容能讓你用java -jar
來運行應用程序。
除了構建插件,代碼清單2-4里的Maven構建說明中還將spring-boot-starter-parent作為上一級,這樣一來就能利用Maven的依賴管理功能,繼承很多常用庫的依賴版本,在你聲明依賴時就不用再去指定版本號了。請注意,這個pom.xml裡的<dependency>
都沒有指定版本。
遺憾的是,Gradle並沒有Maven這樣的依賴管理功能,為此Spring Boot Gradle插件提供了第三個特性,它為很多常用的Spring及其相關依賴模擬了依賴管理功能。其結果就是,代碼清單2-3的build.gradle裡也沒有為各項依賴指定版本。
說起依賴,無論哪個構建說明文件,都只有五個依賴,除了你手工添加的H2之外,其他的Artifact ID都有spring-boot-starter-
前綴。這些都是Spring Boot起步依賴,它們都有助於Spring Boot應用程序的構建。讓我們來看看它們究竟都有哪些好處。