Spring Boot 的核心入口是 SpringApplication.run()。本文基於 Spring Boot 1.5.x/2.x 原始碼,詳細剖析 SpringApplication 物件建構與初始化的完整流程。
@SpringBootApplication
public class MySpringApplication {
public static void main(String[] args) {
SpringApplication.run(MySpringApplication.class, args);
}
}
// 等價於:
SpringApplication app = new SpringApplication(MySpringApplication.class);
app.run(args);
public SpringApplication(Object... sources) {
initialize(sources);
}
private void initialize(Object[] sources) {
// 1. 記錄主要配置來源(@SpringBootApplication 標註的類別)
if (sources != null && sources.length > 0) {
this.sources.addAll(Arrays.asList(sources));
}
// 2. 推斷是否為 Web 環境
this.webEnvironment = deduceWebEnvironment();
// 3. 載入 ApplicationContextInitializer
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
// 4. 載入 ApplicationListener
setListeners((Collection) getSpringFactoriesInstances(
ApplicationListener.class));
// 5. 推斷主類別
this.mainApplicationClass = deduceMainApplicationClass();
}
private final Set
private static final String[] WEB_ENVIRONMENT_CLASSES = {
"javax.servlet.Servlet",
"org.springframework.web.context.ConfigurableWebApplicationContext"
};
private boolean deduceWebEnvironment() {
for (String className : WEB_ENVIRONMENT_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return false; // 缺少任一類別即非 Web 環境
}
}
return true;
}
判斷邏輯:Classpath 中同時存在 Servlet API 與 Spring Web Context 類別 → Web 環境(預設建立 AnnotationConfigEmbeddedWebApplicationContext),否則為非 Web 環境(建立 AnnotationConfigApplicationContext)。
privateCollection extends T> getSpringFactoriesInstances(Class type, Class>[] parameterTypes, Object... args) { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); // 1. 從 META-INF/spring.factories 讀取類別名稱 Set names = new LinkedHashSet<>( SpringFactoriesLoader.loadFactoryNames(type, classLoader)); // 2. 實例化每個類別 List instances = new ArrayList<>(names.size()); for (String name : names) { Class> instanceClass = ClassUtils.forName(name, classLoader); Constructor> constructor = instanceClass.getConstructor(parameterTypes); T instance = (T) constructor.newInstance(args); instances.add(instance); } // 3. 依 @Order 排序 AnnotationAwareOrderComparator.sort(instances); return instances; }
spring-boot-1.3.0.RELEASE.jar/META-INF/spring.factories 中的預設 4 個 Initializer:
| Initializer | 功能 | Order |
|---|---|---|
| ConfigurationWarningsApplicationContextInitializer | 檢測配置問題並發出警告 | Ordered.LOWEST_PRECEDENCE |
| ContextIdApplicationContextInitializer | 設定 ApplicationContext ID | Ordered.LOWEST_PRECEDENCE |
| DelegatingApplicationContextInitializer | 委派給 SpringApplication 的 initializers | Ordered.LOWEST_PRECEDENCE |
| ServerPortInfoApplicationContextInitializer | 記錄伺服器埠資訊 | Ordered.LOWEST_PRECEDENCE |
同樣從 META-INF/spring.factories 載入:
| Listener | 功能 |
|---|---|
| ParentContextCloserApplicationListener | 關閉父上下文 |
| FileEncodingApplicationListener | 設定檔案編碼 |
| AnsiOutputApplicationListener | 控制台 ANSI 色彩輸出 |
| ConfigFileApplicationListener | 載入 application.properties/yml(核心) |
| DelegatingApplicationListener | 事件委派 |
| LiquibaseServiceLocatorApplicationListener | Liquibase 整合 |
| ClasspathLoggingApplicationListener | 早期日誌系統初始化 |
| LoggingApplicationListener | 日誌系統完整初始化 |
private Class> deduceMainApplicationClass() {
try {
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
for (StackTraceElement element : stackTrace) {
if ("main".equals(element.getMethodName())) {
return Class.forName(element.getClassName());
}
}
} catch (ClassNotFoundException ex) {
// 忽略
}
return null;
}
透過堆疊追蹤找到 main() 方法所在類別,用於 Banner 顯示、日誌配置等。
| 屬性 | 值/狀態 | 用途 |
|---|---|---|
| sources | LinkedHashSet<主類別> | Bean 定義來源 |
| webEnvironment | true/false | 決定 ApplicationContext 類型 |
| initializers | List<4個Initializer> | Context 初始化擴充點 |
| listeners | List<8個Listener> | 啟動事件監聽 |
| mainApplicationClass | Class<主類別> | Banner、日誌、主類別識別 |
SpringFactoriesLoader 掃描所有 JAR 的 META-INF/spring.factories,實現零配置擴充AnnotationAwareOrderComparator 確保 Initializer/Listener 執行順序run() 定義啟動骨架,子類別可覆寫特定步驟new SpringApplication(PrimarySource.class)
│
├─▶ sources.add(PrimarySource.class)
│
├─▶ deduceWebEnvironment() → true/false
│ └─▶ ClassUtils.isPresent(Servlet.class) & ConfigurableWebApplicationContext.class
│
├─▶ getSpringFactoriesInstances(ApplicationContextInitializer.class)
│ └─▶ SpringFactoriesLoader.loadFactoryNames()
│ └─▶ 掃描所有 JAR META-INF/spring.factories
│ └─▶ 讀取 key=ApplicationContextInitializer 的值
│ └─▶ 實例化 4 個 Initializer → 排序 → 存入 initializers
│
├─▶ getSpringFactoriesInstances(ApplicationListener.class)
│ └─▶ 同理載入 8 個 Listener → 存入 listeners
│
└─▶ deduceMainApplicationClass()
└─▶ new RuntimeException().getStackTrace() → 找 main() 方法類別
理解 SpringApplication 初始化流程,是掌握 Spring Boot 自動配置、啟動擴充點(Initializer/Listener)、環境判斷等核心機制的基礎。下一篇將深入剖析 run() 方法的完整執行流程。