Spring Boot 應用程式

本節包含直接與 Spring Boot 應用程式相關的主題。

建立您自己的 FailureAnalyzer

FailureAnalyzer 是一種在啟動時攔截例外狀況並將其轉換為人類可讀訊息 (封裝在 FailureAnalysis 中) 的絕佳方法。Spring Boot 為應用程式內容相關的例外狀況、JSR-303 驗證等提供此類分析器。您也可以建立自己的分析器。

AbstractFailureAnalyzerFailureAnalyzer 的便利擴充功能,可檢查例外狀況中是否存在指定的例外狀況類型以進行處理。您可以從此擴充,讓您的實作只有在實際存在例外狀況時才有機會處理例外狀況。如果由於任何原因您無法處理例外狀況,請傳回 null,讓另一個實作有機會處理例外狀況。

FailureAnalyzer 實作必須在 META-INF/spring.factories 中註冊。以下範例註冊 ProjectConstraintViolationFailureAnalyzer

org.springframework.boot.diagnostics.FailureAnalyzer=\
com.example.ProjectConstraintViolationFailureAnalyzer
如果您需要存取 BeanFactoryEnvironment,請在您的 FailureAnalyzer 實作中將它們宣告為建構子引數。

疑難排解自動組態

Spring Boot 自動組態盡力「做正確的事」,但有時事情會失敗,而且可能很難知道原因。

任何 Spring Boot ApplicationContext 中都有一個非常有用的 ConditionEvaluationReport。如果您啟用 DEBUG 日誌記錄輸出,您就可以看到它。如果您使用 spring-boot-actuator (請參閱 Actuator 章節),則還有一個 conditions 端點,可以 JSON 格式呈現報告。使用該端點來偵錯應用程式,並查看 Spring Boot 在執行階段新增 (以及未新增) 了哪些功能。

許多其他問題可以透過查看原始程式碼和 API 文件來解答。閱讀程式碼時,請記住以下經驗法則

  • 尋找名為 *AutoConfiguration 的類別並閱讀其原始碼。特別注意 @Conditional* 註解,以找出它們啟用了哪些功能以及何時啟用。將 --debug 新增至命令列或系統屬性 -Ddebug,以在主控台上取得應用程式中所有自動組態決策的記錄。在已啟用 Actuator 的執行中應用程式中,查看 conditions 端點 (/actuator/conditions 或 JMX 等效端點) 以取得相同的資訊。

  • 尋找 @ConfigurationProperties 類別 (例如 ServerProperties) 並從中讀取可用的外部組態選項。@ConfigurationProperties 註解具有 name 屬性,可作為外部屬性的前置詞。因此,ServerProperties 具有 prefix="server",其組態屬性為 server.portserver.address 和其他屬性。在已啟用 Actuator 的執行中應用程式中,查看 configprops 端點。

  • 尋找在 Binder 上使用 bind 方法,以輕鬆地從 Environment 中提取組態值。它通常與前置詞一起使用。

  • 尋找直接繫結至 Environment@Value 註解。

  • 尋找 @ConditionalOnExpression 註解,這些註解會根據 SpEL 表達式 (通常使用從 Environment 解析的預留位置來評估) 來切換功能的開啟和關閉。

在 Environment 或 ApplicationContext 啟動之前自訂它們

SpringApplication 具有 ApplicationListenersApplicationContextInitializers,用於將自訂項目套用至內容或環境。Spring Boot 從 META-INF/spring.factories 載入許多此類自訂項目以供內部使用。有多種方法可以註冊其他自訂項目

  • 以程式設計方式,針對每個應用程式,在執行 SpringApplication 之前呼叫 addListenersaddInitializers 方法。

  • 以宣告方式,針對所有應用程式,新增 META-INF/spring.factories 並封裝應用程式都用作程式庫的 jar 檔案。

SpringApplication 將一些特殊的 ApplicationEvents 傳送至監聽器 (有些甚至在建立內容之前),然後註冊監聽器以接收 ApplicationContext 發布的事件。如需完整清單,請參閱「Spring Boot 功能」章節中的 應用程式事件和監聽器

也可以在重新整理應用程式內容之前使用 EnvironmentPostProcessor 自訂 Environment。每個實作都應在 META-INF/spring.factories 中註冊,如下列範例所示

org.springframework.boot.env.EnvironmentPostProcessor=com.example.YourEnvironmentPostProcessor

實作可以載入任意檔案並將其新增至 Environment。例如,以下範例從類別路徑載入 YAML 組態檔

  • Java

  • Kotlin

import java.io.IOException;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.boot.env.YamlPropertySourceLoader;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;

public class MyEnvironmentPostProcessor implements EnvironmentPostProcessor {

	private final YamlPropertySourceLoader loader = new YamlPropertySourceLoader();

	@Override
	public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
		Resource path = new ClassPathResource("com/example/myapp/config.yml");
		PropertySource<?> propertySource = loadYaml(path);
		environment.getPropertySources().addLast(propertySource);
	}

	private PropertySource<?> loadYaml(Resource path) {
		Assert.isTrue(path.exists(), () -> "Resource " + path + " does not exist");
		try {
			return this.loader.load("custom-resource", path).get(0);
		}
		catch (IOException ex) {
			throw new IllegalStateException("Failed to load yaml configuration from " + path, ex);
		}
	}

}
import org.springframework.boot.SpringApplication
import org.springframework.boot.env.EnvironmentPostProcessor
import org.springframework.boot.env.YamlPropertySourceLoader
import org.springframework.core.env.ConfigurableEnvironment
import org.springframework.core.env.PropertySource
import org.springframework.core.io.ClassPathResource
import org.springframework.core.io.Resource
import org.springframework.util.Assert
import java.io.IOException

class MyEnvironmentPostProcessor : EnvironmentPostProcessor {

	private val loader = YamlPropertySourceLoader()

	override fun postProcessEnvironment(environment: ConfigurableEnvironment, application: SpringApplication) {
		val path: Resource = ClassPathResource("com/example/myapp/config.yml")
		val propertySource = loadYaml(path)
		environment.propertySources.addLast(propertySource)
	}

	private fun loadYaml(path: Resource): PropertySource<*> {
		Assert.isTrue(path.exists()) { "Resource $path does not exist" }
		return try {
			loader.load("custom-resource", path)[0]
		} catch (ex: IOException) {
			throw IllegalStateException("Failed to load yaml configuration from $path", ex)
		}
	}

}
Environment 已使用 Spring Boot 預設載入的所有常用屬性來源進行準備。因此,可以從環境取得檔案的位置。上述範例在清單的結尾新增 custom-resource 屬性來源,以便在任何其他常用位置中定義的金鑰優先。自訂實作可以定義另一個順序。
雖然在您的 @SpringBootApplication 上使用 @PropertySource 看起來像是載入 Environment 中自訂資源的便利方法,但我們不建議這樣做。此類屬性來源在重新整理應用程式內容之前不會新增至 Environment。這太遲了,無法組態某些屬性,例如在開始重新整理之前讀取的 logging.*spring.main.*

建立 ApplicationContext 階層 (新增父層或根層 Context)

您可以使用 ApplicationBuilder 類別來建立父/子 ApplicationContext 階層。如需詳細資訊,請參閱「Spring Boot 功能」章節中的 Fluent Builder API

建立非 Web 應用程式

並非所有 Spring 應用程式都必須是 Web 應用程式 (或 Web 服務)。如果您想要在 main 方法中執行一些程式碼,但也想要引導 Spring 應用程式以設定要使用的基礎架構,您可以使用 Spring Boot 的 SpringApplication 功能。SpringApplication 會變更其 ApplicationContext 類別,具體取決於它是否認為需要 Web 應用程式。您可以做的第一件事是將伺服器相關的依賴 (例如 servlet API) 從類別路徑中移除。如果您無法做到這一點 (例如,如果您從相同的程式碼庫執行兩個應用程式),則可以明確地在您的 SpringApplication 實例上呼叫 setWebApplicationType(WebApplicationType.NONE),或設定 applicationContextClass 屬性 (透過 Java API 或使用外部屬性)。您想要作為業務邏輯執行的應用程式碼可以實作為 CommandLineRunner 並作為 @Bean 定義放入內容中。