Part III. Using Spring Boot
文檔說明:
- 文檔對應的版本為 2.1.0.M3
- 這不是文檔的完整中文翻譯,也有可能跟原文文字不一一對應,只是我閱讀文檔時候做的簡單筆記
- 如果對應的章節沒有任何中文,有可能是文檔內容比較少,建議直接看原文,或者是我不感興趣的部分
- 目錄標題沒有做翻譯,首先標題一般一眼就能看懂什么意思,不做翻譯還能保證原文意思,其次也方便對應到原文位置
這個篇章主要介紹 Spring Boot 更詳細的特性,包括構建系統,自動配置和部署,最后介紹一些最佳實踐。
13. Build Systems
強烈建議使用構建工具:Maven 或者 Gradle
13.1 Dependency Management
每個 Spring Boot 的發行版本都會提供一個可支持的依賴列表。在實際處理依賴的時候,你不需要提供版本號,版本號也由 Spring Boot 管理了。當你更新了 Spring Boot 的版本,相關的依賴也會更新對應的版本。
你仍然可以覆蓋 Spring Boot 的推薦版本來實現依賴指定的版本,但是 Spring Framework 的版本號就不要隨意更改了。
13.2 Maven
如果 Maven 用戶配置依賴了 spring-boot-starter-parent 項目,那么會繼承其默認約束:
- Java 1.8 的編譯器
- UTF-8 源代碼文件的編碼格式
- 一個依賴管理的片段:管理各依賴版本的默認配置
- repackage 的 Maven 任務
- 智能的 resource 過濾
- 智能的插件配置
- 智能地處理多環境的配置文件:例如 application-dev.properties 和 applicaton-dev.yml
13.2.1 Inheriting the Starter Parent
配置 pom.xml 時候,Spring Boot 需要指定版本,如果是其他的 starter 和依賴,則可以忽略版本號。
<!-- Inherit defaults from Spring Boot -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.0.M3</version>
</parent>
[Spring Boot dependencies pom][spring-boot-dependencies-pom] 支持的屬性配置和依賴的默認版本查看
13.2.2 Using Spring Boot without the Parent POM
并不是每個人都喜歡繼承 spring-boot-starter-parent POM,或者你已經有自己的 parent POM,這種情況你需要顯示配置你的 Maven 配置。
在不繼承 spring-boot-starter-parent POM 的情況下,你可以通過 scope=import 依賴來獲取 Spring Boot 的依賴管理。
<dependencyManagement>
<dependencies>
<dependency>
<!-- Import dependency management from Spring Boot -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.1.0.M3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
上面例子里面,如果你在 dependency 下面繼續添加你的個人依賴,你配置的版本號是不會生效的。為了達到自己配置第三方庫版本號的效果,你需要在配置 spring-boot-dependencies 之前就配置好第三方庫版本。例如,指定 Spring Data release train 庫的版本號如下所示:
<dependencyManagement>
<dependencies>
<!-- Override Spring Data release train provided by Spring Boot -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-releasetrain</artifactId>
<version>Fowler-SR2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.1.0.M3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
13.2.3 Using the Spring Boot Maven Plugin
Spring Boot 提供了一個打包成可執行的 jar 包的 Maven 插件,無論你是否繼承 spring-boot-starter-parent,都需要添加如下配置
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
13.3 Gradle
詳情可查看《Spring Boot Gradle Plugin Reference Guide》文檔
Spring Boot 還寫了一個 jar 包,詳情可查看其 API
13.4 Ant
可以通過 Apache Ant 和 Lvy 來構建 Spring Boot 項目。spring-boot-antlib 也可以幫助 Ant 來構建出可執行的 jar。
13.5 Starters
Startes 是依賴的描述器,代表了一組依賴,使用的時候非常方便。通過 Starters,你可以得到 SPring 和其相關技術的一站式服務,而不需要自己翻閱代碼和配置詳細的依賴。例如你想使用 Spring 和 JPA 來實現數據庫訪問,只需要配置 spring-boot-starter-data-jpa 依賴即可。
Spring Boot Starters 的官方命名都有一定的格式 spring-boot-starter-*
,這可以和 IDE 的自動補全結合,方便開發者編碼。由于你可以創建自己的 Starter,你的 Starter 名稱建議命名的時候項目名稱放在前面,例如 yourproject-spring-boot-starter。
org.springframework.boot 可配置的 Starters 列表。
其他社區貢獻的 Starters 可以參看 GitHub 的 README file。
14. Structuring Your Code
運行 Spring Boot 不需要特定的代碼結構,然而,有些組織代碼的建議。
14.1 Using the "default" Package
你的所有類都建議放到一個特定的 package 里面,沒有指定 package 的類會歸置到 default package,這會在使用 @Component,@EntityScan,@SpringBootApplication 的時候會觸發一些問題。
14.2 Locating the Main Application Class
我們一般建議你把啟動類放在 package 的根目錄,啟動類一般是帶有 main() 方法和 @SpringBootApplication 修飾的類。
如果不想使用 @SpringBootApplication,可以用 @EnableAutoConfiguration 和 @ComponentScan 來替代。
下面是一個代碼結構的例子
com
+- example
+- myapplication
+- Application.java
|
+- customer
| +- Customer.java
| +- CustomerController.java
| +- CustomerService.java
| +- CustomerRepository.java
|
+- order
+- Order.java
+- OrderController.java
+- OrderService.java
+- OrderRepository.java
15. Configuration Classes
Spring Boot 偏向于使用 Java-based 配置。雖然有可能在使用 SpringApplication 的過程中使用 XML 配置,但是我們一般建議你的主配置類是單獨的 @Configuration 類。通常來說,這個主配置類一般還會定義 main 方法。
Tip:很多已經發布到網上的 Spring 應用例子都是使用 XML 配置的,如果可以的話,盡量使用對應的 Java-based 配置。可以嘗試查找 Enable* 的注解。
15.1 Importing Additional Configuration Classes
你沒有必要把所有的 @Configuration 都放到一個類里面,通過 @Import 可以引入其他的 @Configuration 類。或者,你也可以使用 @ComponentScan 來自動掃描所有 Spring 組件,包括 @Configuration 類。
15.2 Importing XML Configuration
如果你一定要使用 XML 配置,我們建議你的主配置類還是 @Configuration 類,然后通過 @ImportResource 注解來加載 XML 配置文件。
16. Auto-configuration
Spring Boot 自動裝配會根據你應用的 jar 依賴來自動配置你的 Spring 應用。例如,如果 HSQLDB 在 classpath 上,而你還沒有手動配置任何的數據庫連接類,那么 Spring Boot 會自動裝配一個嵌入式的數據庫。
你需要選擇是否需要這個自動裝配功能,在你其中的一個 @Configuration 類是否添加 @EnableAutoConfiguration 或者 @SpringBootApplication 注解。
16.1 Gradually Replacing Auto-configuration
Auto-configuration 是非侵入性的。在任何時候,你可以定義自己的配置來替代 auto-configuration 的特定配置。例如,你添加了自己的 DataSource bean,那么默認的嵌入式數據庫就不會再啟用。
如果你需要了解 auto-configuration 都做了哪些默認配置,你可以在啟動應用的時候加上 --debug 參數,這樣就可以在日志或者控制臺上查看了。
16.2 Disabling Specific Auto-configuration Classes
如果你不想使用某個 auto-configuration 類,可以在 @EnableAutoConfiguration 注解添加 exclude 屬性來禁用這個類,代碼示例如下:
import org.springframework.boot.autoconfigure.*;
import org.springframework.boot.autoconfigure.jdbc.*;
import org.springframework.context.annotation.*;
@Configuration
@EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
public class MyConfiguration {
}
如果這個類不在 classpath 上,可以配置其 excludeName 屬性,內容就填寫這個類的完整類名。最后,你還可以通過 spring.autoconfigure.exclude 配置項來配置一個禁用類的列表。
17. Spring Beans and Dependency Injection
你可以使用任意 Spring 的特性來定義你的 beans 和它們的注入依賴。為簡單起見,我們經常發現使用 @ComponentScan 和 @Atuowired 都沒有問題。
如果你的代碼如上面建議一樣,可以在根目錄找到主配置類,你在添加 @ComponentScan 的時候可以不用設置參數。你應用的所有組件 (@Component,
@Service, @Repository, @Controller etc.) 都會被自動注冊為 Spring Beans。
給個 @Service 的例子:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class DatabaseAccountService implements AccountService {
private final RiskAssessor riskAssessor;
@Autowired
public DatabaseAccountService(RiskAssessor riskAssessor) {
this.riskAssessor = riskAssessor;
}
// ...
}
如果你的類只有一個構造方法,你還可以省略 @Autowired 注解,如下所示:
@Service
public class DatabaseAccountService implements AccountService {
private final RiskAssessor riskAssessor;
public DatabaseAccountService(RiskAssessor riskAssessor) {
this.riskAssessor = riskAssessor;
}
// ...
}
18. Using the @SpringBootApplication Annotation
很多 Spring Boot 開發者都愿意他們的應用使用到自動裝配,組件掃描和定義更多額外的屬性。一個 @SpringBootApplication 注解可以被用來啟用這三個特性:
- @EnableAutoConfiguration:啟用 Spring Boot 的自動裝配機制
- @ComponentScan:啟用組件掃描
- @Configuration:允許在 context 注冊 bean 或者引入額外的配置類
這個 @SpringBootApplication 注解就等于 @Configuration, @EnableAutoConfiguration 和 @ComponentScan 注解的效果,如下所示:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication // same as @Configuration @EnableAutoConfiguration @ComponentScan
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@SpringBootApplication 也提供了別名來配置 @EnableAutoConfiguration 和 @ComponentScan 相關的屬性。
這些特性都不是強制需要配置的,你可以其他相同功能的注解來替換 @SpringBootApplication 注解來實現你的需求。例如,你不想要組件掃描的功能,代碼如下:
import org.springframework.boot.SpringApplication;
import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@EnableAutoConfiguration
@Import({ MyConfig.class, MyAnotherConfig.class })
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
19. Running Your Application
把你的應用打包成 jar 和內嵌的 HTTP 服務,其中一個最大的好處就是你可以很方便地運行你的應用,而不需要特殊的 IDE 或其他插件。
當然,你也可以打包成 war 文件,你需要查閱其他的文檔。
19.1 Running from an IDE
找到 "Run as a Java Application" 的菜單并選擇執行
19.2 Running as a Packaged Application
如果你有使用 Maven 或者 Gradle 構建工具生成了一個可執行的 jar 文件,你可以通過 java -jar 來運行應用。
$ java -jar target/myapplication-0.0.1-SNAPSHOT.jar
也可以啟用遠程調試
$ java -Xdebug -Xrunjdwp:server=y,transport=dt_socket,address=8000,suspend=n -jar target/myapplication-0.0.1-SNAPSHOT.jar
19.3 Using the Maven Plugin
Spring Boot Maven 插件包含了一個 run 程序,用來快速編譯和運行你的應用。
$ mvn spring-boot:run
你也可以使用 MAVEN_OPTS 來操作系統環境變量
$ export MAVEN_OPTS=-Xmx1024m
19.4 Using the Gradle Plugin
Spring Boot Gradle 插件包含了一個 bootRun 程序。
$ gradle bootRun
你也可以使用 JAVA_OPTS 來操作系統環境變量
$ export JAVA_OPTS=-Xmx1024m
19.5 Hot Swapping
由于 Spring Boot 應用只是普通的Java應用程序,所以 JVM Hot Swapping 可以實現。JVM Hot Swapping 在某種程度上只能做到替換字節碼文件,如果需要一個完整的解決方案,可以參考一下 JRebel。
spring-boot-devtools 模塊也提供了一些可以快速重啟應用的支持,可以查看 Chapter 20,或者 Chapter 89 的 Hot Swapping。
20. Developer Tools
Spring Boot 提供了一個可以用來提升應用開發體驗的工具集 spring-boot-devtools,這個工具集默認是不啟用的,有需要可以添加如下配置
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
20.1 Property Defaults
緩存在生產環境是很有用的,但是在開發環境卻相反,因為你有可能不能及時看到你修改代碼后的效果,所以 spring-boot-devtools 默認不啟用緩存。
可以配置 application.properties 的 spring.thymeleaf.cache 項來控制緩存。
spring.http.log-request-details:可以記錄請求內容,處理的控制器和響應內容。
spring.devtools.addproperties:取消 DevTools 的默認配置。
更多默認的屬性可以參看 DevToolsPropertyDefaultsPostProcessor
20.2 Automatic Restart
當 classpath 上面的文件發生改動時,DevTools 會自動重啟應用。一般情況下,這對于快速獲取反饋很有幫助。
DevTools 會監控 classpath 所有的資源文件,保存一個類文件或者在IDE操作 Build Project 都會觸發 restart。
DevTools 和 LiveReload 搭配使用效果更好,LiveReload 在 20.3 節有詳細介紹。如果有使用 JRebel,自動重啟機制會不啟用,DevTools 的其他功能,例如 LiveReload 和 property default 仍會生效。
DevTools 在重啟應用的時候是通過 ApplicationContext's shutdown hook 來實現關閉應用,如果你有 SpringApplication.setRegisterShutdownHook(false)
的代碼,重啟機制會運行異常。
Restart vs Reload
Restart 機制是由 Spring Boot 通過兩個 ClassLoader 來實現的,不需要依賴其他的組件。一些不會改動的類,例如第三庫的 jars,會被加載到 Base ClassLoader。那些你正在開發的代碼類文件,會被加載到 Restart ClassLoader。當觸發 Restart 時,Restart ClassLoader 會被重新創建,Base ClassLoader 不做改動。這個機制會讓其 Restart 操作比冷啟動會快很多。
如果你覺得上面的機制還是不夠快,你可以借助其他組件(例如 JRebel)來實現 Reload。替換其對應的 class 字節碼文件來實現。
20.2.1 Logging changes in condition evaluation
每次重啟后,DevTools 默認會記錄增量變化的報告,可通過下面的配置項來禁用
spring.devtools.restart.log-condition-evaluation-delta=false
20.2.2 Excluding Resources
想對某些文件修改后不觸發重啟操作,可以參看下面的配置
# 修改靜態文件的默認配置
spring.devtools.restart.exclude=static/**,public/**
# 在原有的基礎上增加例外文件的配置
spring.devtools.restart.additional-exclude=filePath*
20.2.3 Watching Additional Paths
配置 spring.devtools.restart.additionalpaths 項
一般會和 spring.devtools.restart.exclude 配置項搭配使用
20.2.4 Disabling Restart
配置 spring.devtools.restart.enabled 項來不啟用重啟機制,這種情況下,Restart ClassLoader 還是會被初始化,只是他不會在監控任何文件的改動。
如果想完全禁用重啟機制,那么在調用 SpringApplication.run() 方法前設置其變量為 false。
public static void main(String[] args) {
System.setProperty("spring.devtools.restart.enabled", "false");
SpringApplication.run(MyApp.class, args);
}
20.2.5 Using a Trigger File
你經常使用 IDE 來編碼,可能更希望在特定的時機重啟,可以通過監控一個特殊文件來實現。
配置 spring.devtools.restart.trigger-file 項
20.2.6 Customizing the Restart Classloader
上面的章節有描述 Restart ClassLoader 和 Base ClassLoader,正常情況下,你在 IDE 的代碼都會被加載到 Restart ClassLoader,如果你在 IDE 里面有多個模塊,而有些模塊是不經常改動,那么你可以通過添加 META-INF/spring-devtools.properties 文件來配置特殊的邏輯。
spring-devtools.properties 文件的配置內容都必須以 restart.exclude 或者 restart.include 開頭,restart.exclude 表示加載到 Base ClassLoader,restart.exclude 表示加載到 Restart ClassLoader。
restart.exclude.companycommonlibs=/mycorp-common-[\\w-]+\.jar
restart.include.projectcommon=/mycorp-myproj-[\\w-]+\.jar
20.2.7 Known Limitations
重啟功能在某些場景下運行不是很好,例如在用 ObjectInputStream 做對象轉換的時候。
20.3 LiveReload
spring-boot-devtools 模塊有一個嵌入式的 LiveReload 服務,這個服務可以用來在資源文件改變后觸發瀏覽器刷新頁面。LiveReload 瀏覽器插件可以從 livereload.com 獲取,支持 Chrome,Firefox 和 Safari。
如果不想啟用 LiveReload 服務,把 spring.devtools.livereload.enabled 屬性配置為 false 即可。
同一時間你只能運行一個 LiveReload 服務,所以在啟動你的應用之前,確認是否有其他的 LiveReload 服務在運行。如果你在 IDE 啟動多個應用,只有第一個啟動的應用可以正常使用 LiveReload 服務。
20.4 Global Settings
在用戶目錄添加 ~/.spring-boot-devtools.properties
文件,里面的配置會對這臺機器上面所有的 Spring Boot 應用都會生效。
20.5 Remote Applications
spring-boot-devtools 并不只是限制在本地使用,還有一些功能是針對遠程應用的。遠程支持的功能是可選的,若要啟用這個功能,依賴配置如下:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludeDevtools>false</excludeDevtools>
</configuration>
</plugin>
</plugins>
</build>
還需要設置 spring.devtools.remote.secret 屬性,例如:
spring.devtools.remote.secret=mysecret
Warning: 啟用 spring-boot-devtools 的遠程功能會有安全風險,生產環境不能開啟該功能。
遠程功能包含兩個部分:一個是接收連接的服務端和一個在你 IDE 運行的客戶端。當 spring.devtools.remote.secret 屬性被設置有值的時候,服務端會自動啟用該功能,客戶端需要手動啟動。
20.5.1 Running the Remote Client Application
遠程功能的客戶端就是被設計在 IDE 運行的。在客戶端,你需要使用和遠程項目相同的 classpath 來運行org.springframework.boot.devtools.RemoteSpringApplication。客戶端應用唯一需要的參數就是它要連接遠程項目的 URL。
例如,你在使用 Eclipse 或者 STS,有一個項目 my-app ,部署到了 Cloud Foundry,那你需要進行如下操作:
- Select Run Configurations… from the Run menu.
- Create a new Java Application “launch configuration”.
- Browse for the my-app project.
- Use org.springframework.boot.devtools.RemoteSpringApplication as the main class.
- Add https://myapp.cfapps.io to the Program arguments (or whatever your remote URL is).
一個運行遠程功能的客戶端啟動日志大概如下:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ ___ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | | _ \___ _ __ ___| |_ ___ \ \ \ \
\\/ ___)| |_)| | | | | || (_| []::::::[] / -_) ' \/ _ \ _/ -_) ) ) ) )
' |____| .__|_| |_|_| |_\__, | |_|_\___|_|_|_\___/\__\___|/ / / /
=========|_|==============|___/===================================/_/_/_/
:: Spring Boot Remote :: 2.1.0.M3
2015-06-10 18:25:06.632 INFO 14938 --- [ main] o.s.b.devtools.RemoteSpringApplication :
spring-boot-devtools/target/classes started by pwebb in /Users/pwebb/projects/spring-boot/code/springboot-
samples/spring-boot-sample-devtools)
2015-06-10 18:25:06.671 INFO 14938 --- [ main] s.c.a.AnnotationConfigApplicationContext :
Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@2a17b7b6: startup
date [Wed Jun 10 18:25:06 PDT 2015]; root of context hierarchy
2015-06-10 18:25:07.043 WARN 14938 --- [ main] o.s.b.d.r.c.RemoteClientConfiguration : The
connection to http://localhost:8080 is insecure. You should use a URL starting with 'https://'.
2015-06-10 18:25:07.074 INFO 14938 --- [ main] o.s.b.d.a.OptionalLiveReloadServer :
LiveReload server is running on port 35729
2015-06-10 18:25:07.130 INFO 14938 --- [ main] o.s.b.devtools.RemoteSpringApplication :
Started RemoteSpringApplication in 0.74 seconds (JVM running for 1.105)
Note: 因為客戶端使用的 classpath 和真正的應用是一樣的,所以它能直接讀取到應用的屬性配置。這就是 spring.devtools.remote.secret 配置項如何被讀取并傳給服務端認證的原理。
Tip: 建議使用 https 協議作為傳輸協議,那么傳輸內容是加密的,所以密碼等信息就不會被攔截。
Tip: 如果你通過代理來訪問服務端,那么需要配置 spring.devtools.remote.proxy.host 和 spring.devtools.remote.proxy.port 這兩個屬性。
20.5.2 Remote Update
客戶端會監控你應用 classpath 的文件變化,這個本地重啟(20.2節 Automatic Restart)的機制一樣。所有的文件更新都會推送到服務端,必要時會觸發重啟操作。這在云服務上迭代開發一個功能的場景下會非常有幫助,因為通常來說,服務端的更新和重啟會比全部重啟和循環部署快很多。
Note: 文件只有在客戶端運行時才會被監控,如果你在啟動客戶端前修改的文件,這個變化便不會推送到服務端。
21. Packaging Your Application for Production
Executable jar 可以用來發布到生產環境。由于他們都自帶容器,所以非常適用于基于云平臺的部署。
更多關于可用于生產的功能,可以考慮添加 spring-boot-actuator
庫,可參看文檔 Part V, “Spring Boot Actuator: Production-ready features” 。
22. What to Read Next
你現在應該知道如何使用 Spring Boot 和應該遵循的一些最佳實踐,可以繼續深入了解 Spring Boot 特定的功能,或者你跳過這部分去了解關于生產部署方面的功能。