SpringBoot應(yīng)用啟動機(jī)制的使用詳解
IDEA中啟動SpringBoot的過程
1. 預(yù)啟動階段
1.1 環(huán)境檢測與驗(yàn)證
// IDEA 執(zhí)行的前置檢查
- - JDK 版本兼容性驗(yàn)證
- - 項目依賴完整性檢查
- - Spring Boot 版本與插件匹配
- - 構(gòu)建工具配置驗(yàn)證(Maven/Gradle)
- - 應(yīng)用配置文件語法檢查
1.2 類路徑構(gòu)建
類路徑組成:
├── 項目編譯輸出目錄 (target/classes 或 build/classes)
├── 依賴庫 (Maven: ~/.m2/repository, Gradle: ~/.gradle/caches)
├── 資源文件 (src/main/resources)
├── 測試資源文件 (src/test/resources) [測試時]
└── IDEA 特定模塊路徑
2. Spring Boot 應(yīng)用啟動核心流程
2.1 SpringApplication 初始化階段
public class SpringApplication {
public SpringApplication(Class<?>... primarySources) {
// 1. 主配置類存儲
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 2. 推斷應(yīng)用類型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 3. 加載 ApplicationContextInitializer
setInitializers(getSpringFactoriesInstances(
ApplicationContextInitializer.class));
// 4. 加載 ApplicationListener
setListeners(getSpringFactoriesInstances(ApplicationListener.class));
// 5. 推斷主應(yīng)用類
this.mainApplicationClass = deduceMainApplicationClass();
}
}
2.2 運(yùn)行階段詳細(xì)分解
public ConfigurableApplicationContext run(String... args) {
// 階段 1: 啟動準(zhǔn)備
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 階段 2: 監(jiān)聽器通知 - ApplicationStartingEvent
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
// 階段 3: 環(huán)境準(zhǔn)備
ApplicationArguments applicationArguments =
new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
// 階段 4: 配置忽略的 Bean 信息
configureIgnoreBeanInfo(environment);
// 階段 5: 打印 Banner
Banner printedBanner = printBanner(environment);
// 階段 6: 創(chuàng)建應(yīng)用上下文
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
// 階段 7: 準(zhǔn)備上下文
prepareContext(context, environment, listeners,
applicationArguments, printedBanner);
// 階段 8: 刷新上下文(核心)
refreshContext(context);
// 階段 9: 刷新后處理
afterRefresh(context, applicationArguments);
// 階段 10: 啟動完成通知
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
// 階段 11: 發(fā)布 ApplicationReadyEvent
listeners.started(context);
// 階段 12: 執(zhí)行 Runner Bean
callRunners(context, applicationArguments);
// 階段 13: 發(fā)布 ApplicationStartedEvent
listeners.ready(context, stopWatch);
} catch (Throwable ex) {
handleRunFailure(context, listeners, ex);
throw new IllegalStateException(ex);
}
return context;
}
3. 上下文刷新詳細(xì)過程
3.1 BeanFactory 初始化流程
// AbstractApplicationContext.refresh() 完整流程
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
// 步驟 1: 準(zhǔn)備刷新 - 設(shè)置啟動日期、激活狀態(tài)等
prepareRefresh();
// 步驟 2: 獲取新的 BeanFactory
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 步驟 3: 準(zhǔn)備 BeanFactory 使用
prepareBeanFactory(beanFactory);
try {
// 步驟 4: 允許 BeanFactory 后處理
postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
// 步驟 5: 調(diào)用 BeanFactoryPostProcessor
invokeBeanFactoryPostProcessors(beanFactory);
// 步驟 6: 注冊 BeanPostProcessor
registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
// 步驟 7: 初始化消息源
initMessageSource();
// 步驟 8: 初始化事件廣播器
initApplicationEventMulticaster();
// 步驟 9: 初始化特殊 Bean(模板方法)
onRefresh();
// 步驟 10: 注冊監(jiān)聽器
registerListeners();
// 步驟 11: 完成 BeanFactory 初始化
finishBeanFactoryInitialization(beanFactory);
// 步驟 12: 完成刷新
finishRefresh();
} catch (BeansException ex) {
// 異常處理...
} finally {
resetCommonCaches();
contextRefresh.end();
}
}
}
3.2 Bean 創(chuàng)建生命周期
// 單個 Bean 的完整創(chuàng)建過程
1. 實(shí)例化 Bean (構(gòu)造函數(shù)調(diào)用)
2. 屬性注入 (@Autowired, @Value, @Resource)
3. Aware 接口回調(diào) (BeanNameAware, BeanFactoryAware, ApplicationContextAware)
4. BeanPostProcessor.postProcessBeforeInitialization()
5. @PostConstruct 方法執(zhí)行
6. InitializingBean.afterPropertiesSet() 執(zhí)行
7. 自定義初始化方法 (init-method)
8. BeanPostProcessor.postProcessAfterInitialization()
9. Bean 就緒,加入單例池
10. 應(yīng)用場景:
- 單例 Bean: 啟動時創(chuàng)建
- 原型 Bean: 每次獲取時創(chuàng)建
- 延遲加載: 第一次使用時創(chuàng)建
4. Spring Boot 自動配置機(jī)制
4.1 條件化配置加載
// 自動配置原理 @SpringBootApplication ├── @SpringBootConfiguration ├── @EnableAutoConfiguration │ └── @Import(AutoConfigurationImportSelector.class) └── @ComponentScan // AutoConfigurationImportSelector 工作流程: 1. 加載 META-INF/spring.factories 中所有 EnableAutoConfiguration 配置 2. 根據(jù)條件注解過濾: - @ConditionalOnClass - @ConditionalOnBean - @ConditionalOnProperty - @ConditionalOnWebApplication - @ConditionalOnMissingBean 3. 按 @AutoConfigureOrder、@Order 排序 4. 去重并應(yīng)用配置類
4.2 內(nèi)嵌 Web 服務(wù)器啟動
// Tomcat 啟動詳細(xì)過程 1. ServletWebServerApplicationContext.onRefresh() 2. createWebServer() 創(chuàng)建 WebServer 3. TomcatServletWebServerFactory.getWebServer() - 創(chuàng)建 Tomcat 實(shí)例 - 配置 Engine 和 Host - 創(chuàng)建 Connector(配置端口、協(xié)議等) - 創(chuàng)建 Context 并配置 - 加載 DispatcherServlet - 配置 Session、ErrorPage 等 4. 啟動 Tomcat - 啟動 Connector 監(jiān)聽端口 - 啟動 Engine 處理請求 5. 發(fā)布 ServletWebServerInitializedEvent
服務(wù)器部署啟動的詳細(xì)過程
1. 打包與部署準(zhǔn)備
1.1 可執(zhí)行 JAR 結(jié)構(gòu)
my-application.jar
├── META-INF/
│ └── MANIFEST.MF
├── BOOT-INF/
│ ├── classes/ # 應(yīng)用類文件
│ │ ├── com/yourcompany/Application.class
│ │ └── application.properties
│ └── lib/ # 依賴庫
│ ├── spring-boot-2.7.x.jar
│ ├── spring-core-5.3.x.jar
│ └── ...
└── org/springframework/boot/loader/
├── JarLauncher.class
└── LaunchedURLClassLoader.class
1.2 啟動腳本示例
#!/bin/bash # 生產(chǎn)環(huán)境啟動腳本 # JVM 參數(shù)配置 JAVA_OPTS="-server -Xms2g -Xmx2g -XX:+UseG1GC" JAVA_OPTS="$JAVA_OPTS -XX:MaxGCPauseMillis=200" JAVA_OPTS="$JAVA_OPTS -Dspring.profiles.active=prod" JAVA_OPTS="$JAVA_OPTS -Dlogging.file=/var/log/myapp/application.log" # 啟動應(yīng)用 java $JAVA_OPTS -jar my-application.jar
2. 生產(chǎn)環(huán)境啟動流程
2.1 啟動類加載器機(jī)制
// Spring Boot Launcher 工作機(jī)制
JarLauncher -> LaunchedURLClassLoader
↓
加載 BOOT-INF/classes 和 BOOT-INF/lib/*.jar
↓
反射調(diào)用應(yīng)用的 main 方法
↓
后續(xù)流程與 IDEA 啟動相同
2.2 生產(chǎn)環(huán)境特定配置
# application-prod.yml
spring:
datasource:
url: jdbc:mysql://prod-db:3306/myapp
username: ${DB_USERNAME}
password: ${DB_PASSWORD}
redis:
host: redis-cluster
port: 6379
server:
port: 8080
compression:
enabled: true
servlet:
session:
timeout: 30m
management:
endpoints:
web:
exposure:
include: health,info,metrics
endpoint:
health:
show-details: always
本地啟動與服務(wù)器啟動的異同點(diǎn)對比
1. 環(huán)境配置差異
| 特性 | IDEA 本地啟動 | 服務(wù)器部署啟動 |
|---|---|---|
| 類加載機(jī)制 | 標(biāo)準(zhǔn) ClassLoader | LaunchedURLClassLoader |
| 配置文件加載 | 文件系統(tǒng)直接讀取 | JAR 包內(nèi)資源讀取 |
| 熱部署支持 | DevTools 自動重啟 | 需要手動重啟 |
| 調(diào)試支持 | 完整調(diào)試功能 | 遠(yuǎn)程調(diào)試需配置 |
| 資源監(jiān)控 | IDEA 內(nèi)置工具 | JMX/Actuator 監(jiān)控 |
2. 性能特征對比
2.1 啟動時間分析
// 本地開發(fā)環(huán)境(IDEA) 啟動階段 | 時間占比 ------------------------------------ 類路徑掃描和加載 | 15-20% Bean 定義解析 | 20-25% Bean 實(shí)例化和依賴注入 | 30-35% Web 服務(wù)器啟動 | 15-20% 其他初始化 | 10-15% // 生產(chǎn)服務(wù)器環(huán)境 啟動階段 | 時間占比 ------------------------------------ JAR 解壓和類加載 | 25-30% Bean 定義解析 | 20-25% Bean 實(shí)例化和依賴注入 | 25-30% Web 服務(wù)器啟動 | 15-20% 其他初始化 | 5-10%
2.2 內(nèi)存使用對比
// 開發(fā)環(huán)境典型內(nèi)存配置 -Xms512m -Xmx1024m -XX:MaxMetaspaceSize=256m // 生產(chǎn)環(huán)境典型內(nèi)存配置 -Xms2g -Xmx2g -XX:MaxMetaspaceSize=512m -XX:+UseG1GC -XX:MaxGCPauseMillis=200
3. 配置管理差異
3.1 配置文件加載策略
# 開發(fā)環(huán)境配置優(yōu)先級
1. @TestPropertySource
2. 命令行參數(shù) (IDEA Run Configuration)
3. SPRING_APPLICATION_JSON
4. ServletConfig 初始化參數(shù)
5. ServletContext 初始化參數(shù)
6. JNDI 屬性
7. Java 系統(tǒng)屬性
8. 操作系統(tǒng)環(huán)境變量
9. random.* 屬性
10. application-{profile}.properties/yml
11. application.properties/yml
12. @PropertySource
13. 默認(rèn)屬性
# 生產(chǎn)環(huán)境配置優(yōu)先級
1. 命令行參數(shù) (啟動腳本)
2. SPRING_APPLICATION_JSON
3. Java 系統(tǒng)屬性
4. 操作系統(tǒng)環(huán)境變量
5. random.* 屬性
6. application-{profile}.properties/yml (打包在JAR內(nèi))
7. application.properties/yml (打包在JAR內(nèi))
8. 默認(rèn)屬性
3.2 日志配置差異
# 開發(fā)環(huán)境日志配置
logging:
level:
com.yourcompany: DEBUG
org.springframework: INFO
pattern:
console: "%clr(%d{HH:mm:ss.SSS}){faint} %clr(%-5level) %clr(${PID}){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n%wEx"
# 生產(chǎn)環(huán)境日志配置
logging:
file:
path: /var/log/myapp
name: /var/log/myapp/application.log
level:
com.yourcompany: INFO
org.springframework: WARN
pattern:
file: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"
console: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"
4. 監(jiān)控和管理差異
4.1 開發(fā)環(huán)境監(jiān)控
// IDEA 內(nèi)置工具 - 內(nèi)存使用情況實(shí)時監(jiān)控 - CPU 使用率分析 - 線程狀態(tài)查看 - 斷點(diǎn)調(diào)試和變量查看 - 方法執(zhí)行時間分析 // Spring Boot DevTools - 自動重啟 - LiveReload - 全局配置 - 遠(yuǎn)程調(diào)試支持
4.2 生產(chǎn)環(huán)境監(jiān)控
# Spring Boot Actuator 配置
management:
endpoints:
web:
exposure:
include: health,info,metrics,env,beans
base-path: /manage
endpoint:
health:
show-details: always
probes:
enabled: true
metrics:
enabled: true
metrics:
export:
prometheus:
enabled: true
# 健康檢查配置
spring:
boot:
admin:
client:
url: http://monitoring-server:8080
性能優(yōu)化建議
1. 啟動性能優(yōu)化
1.1 類路徑優(yōu)化
// 減少不必要的依賴
@SpringBootApplication
// 排除不必要的自動配置
@EnableAutoConfiguration(exclude = {
DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAuto.class
})
public class Application {
// 延遲初始化配置
public static void main(String[] args) {
SpringApplication app = new SpringApplication(Application.class);
app.setLazyInitialization(true); // 延遲初始化
app.run(args);
}
}
1.2 Bean 初始化優(yōu)化
@Component
public class HeavyBean {
@PostConstruct
public void init() {
// 異步初始化耗時操作
CompletableFuture.runAsync(() -> {
// 耗時初始化邏輯
heavyInitialization();
});
}
// 使用 @Lazy 延遲加載
@Bean
@Lazy
public ExpensiveService expensiveService() {
return new ExpensiveService();
}
}
2. 內(nèi)存使用優(yōu)化
2.1 JVM 參數(shù)調(diào)優(yōu)
# 生產(chǎn)環(huán)境推薦配置
java -server -Xms2g -Xmx2g \
-XX:+UseG1GC -XX:MaxGCPauseMillis=200 \
-XX:InitiatingHeapOccupancyPercent=45 \
-XX:+ExplicitGCInvokesConcurrent \
-Xlog:gc*:file=/var/log/myapp/gc.log:time,uptime,level,tags:filecount=5,filesize=10m \
-jar my-application.jar
常見問題與解決方案
1. 啟動失敗問題
1.1 類沖突問題
// 解決方案:排除沖突依賴
<dependency>
<groupId>com.some.library</groupId>
<artifactId>problematic-lib</artifactId>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
// 或者使用 dependencyManagement 統(tǒng)一版本
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-bom</artifactId>
<version>2.13.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
1.2 配置加載問題
# 配置加載順序問題解決方案
# 1. 使用明確的配置文件
spring.config.location=classpath:/,classpath:/config/,file:./,file:./config/
# 2. 環(huán)境變量覆蓋
export SPRING_APPLICATION_JSON='{"server":{"port":8080}}'
# 3. 配置屬性驗(yàn)證
@Component
@ConfigurationProperties(prefix = "app.datasource")
@Validated
public class DataSourceProperties {
@NotEmpty
private String url;
// getters and setters
}
2. 性能問題診斷
2.1 啟動時間分析
// 啟用啟動時間監(jiān)控
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(Application.class);
// 添加啟動監(jiān)聽器記錄時間
app.addListeners(new ApplicationListener<ApplicationReadyEvent>() {
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
// 記錄啟動時間
log.info("Application started in {} seconds",
ManagementFactory.getRuntimeMXBean().getUptime() / 1000.0);
}
});
app.run(args);
}
}
// 或者使用 Spring Boot 的啟動指標(biāo)
management.endpoints.web.exposure.include=startup
總結(jié)
以上為個人經(jīng)驗(yàn),希望能給大家一個參考,也希望大家多多支持腳本之家。
- SpringBoot啟動報錯Failed?to?configure?a?DataSource解決方案
- SpringBoot啟動時將數(shù)據(jù)庫數(shù)據(jù)預(yù)加載到Redis緩存的幾種實(shí)現(xiàn)方案
- Springboot啟動類和啟動過程超詳細(xì)講解
- SpringBoot @ConfigurationProperties + Validation實(shí)現(xiàn)啟動期校驗(yàn)解決方案
- springboot啟動讀取外部配置文件實(shí)現(xiàn)方式
- SpringBoot項目啟動內(nèi)存占用過高問題及解決
- SpringBoot工程啟動時自動執(zhí)行任務(wù)實(shí)現(xiàn)方式
相關(guān)文章
IDEA里找不到Maven的有效解決辦法(小白超詳細(xì))
這篇文章主要給大家介紹了關(guān)于IDEA里找不到Maven的有效解決辦法,文中通過圖文將解決的辦法介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-07-07
從Myeclipse 導(dǎo)入到eclipse中無法識別為 web項目 問題的解決步驟
這篇文章主要介紹了從Myeclipse 導(dǎo)入到eclipse中無法識別為 web項目 問題的解決步驟,需要的朋友可以參考下2018-05-05
Springboot+Thymeleaf+Jpa實(shí)現(xiàn)登錄功能(附源碼)
最近有學(xué)習(xí)到關(guān)于Springboot+Thymeleaf+Jpa的綜合運(yùn)用知識,因此想寫一個簡單的登錄界面來嘗試一下,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-05-05
spring?boot集成jasypt?并實(shí)現(xiàn)自定義加解密的詳細(xì)步驟
由于項目中的配置文件?配置的地方過多,現(xiàn)將配置文件統(tǒng)一放到nacos上集中管理?且密碼使用加密的方式放在配置文件中,配置文件的加密使用加密庫jasypt,本文給大家介紹spring boot集成jasypt并實(shí)現(xiàn)自定義加解密,感興趣的朋友一起看看吧2023-08-08
Hadoop 使用IntelliJ IDEA 進(jìn)行遠(yuǎn)程調(diào)試代碼的配置方法
這篇文章主要介紹了Hadoop 使用IntelliJ IDEA 進(jìn)行遠(yuǎn)程調(diào)試代碼的配置方法,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-04-04
SpringBoot中Mybatis + Druid 數(shù)據(jù)訪問的詳細(xì)過程
Spring Boot 底層都是采用 SpringData 的方式進(jìn)行統(tǒng)一處理各種數(shù)據(jù)庫,SpringData也是Spring中與SpringBoot、SpringCloud 等齊名的知名項目,下面看下SpringBoot Mybatis Druid數(shù)據(jù)訪問的詳細(xì)過程,感興趣的朋友一起看看吧2021-11-11

