讀古今文學網 > Spring Boot實戰 > 2.1 運用Spring Boot >

2.1 運用Spring Boot

你正在閱讀本書,說明你是一位讀書人。也許你是一個書蟲,博覽群書;也許你只讀自己需要的東西,拿起本書只是為了知道怎麼用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:這個不起眼的小註解也可以稱為@Abracadabra2,就是這一行配置開啟了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應用程序的構建。讓我們來看看它們究竟都有哪些好處。