Spring 的啟動(dòng)流程,90% 的 Java 程序員都說(shuō)不出來(lái)!
在開(kāi)始今天的文章之前,你能先想想 Spring的啟動(dòng)流程嗎?
是的...
毫不夸張,很多號(hào)稱(chēng)使用 Spring多年的程序員,答不出這個(gè)問(wèn)題。
這篇文章,我們來(lái)聊聊 Spring框架的啟動(dòng)過(guò)程,本文會(huì)逐步拆解 Spring的啟動(dòng)流程,分析其背后的原理,并通過(guò)實(shí)例來(lái)加深理解。最后,還會(huì)進(jìn)行一個(gè)小結(jié),幫助大家梳理關(guān)鍵點(diǎn)。
一、Spring啟動(dòng)的整體流程
對(duì)于 Spring應(yīng)用程序的啟動(dòng)過(guò)程,我們可以總結(jié)成以下 6個(gè)主要步驟:
- 引導(dǎo)階段:?jiǎn)?dòng)器(如Spring Boot的SpringApplication)被調(diào)用,初始化應(yīng)用上下文。
- 環(huán)境準(zhǔn)備:加載配置文件,設(shè)置環(huán)境屬性。
- 創(chuàng)建應(yīng)用上下文:根據(jù)配置創(chuàng)建適當(dāng)?shù)腁pplicationContext(如AnnotationConfigApplicationContext)。
- 注冊(cè)Bean定義:掃描包路徑,找到并注冊(cè)所有的Bean定義。
- 刷新上下文:執(zhí)行Bean實(shí)例化、依賴(lài)注入、初始化等步驟。
- 啟動(dòng)完成:應(yīng)用程序正式啟動(dòng),可以接受請(qǐng)求或執(zhí)行任務(wù)。
接下來(lái),我們將逐步深入每一個(gè)階段。
二、關(guān)鍵步驟詳解
1. 引導(dǎo)階段
在Spring Boot項(xiàng)目中,啟動(dòng)通常從一個(gè)主類(lèi)的main方法開(kāi)始,例如:
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
這里,SpringApplication.run()方法負(fù)責(zé)引導(dǎo)整個(gè) Spring應(yīng)用的啟動(dòng)過(guò)程。它的主要作用是創(chuàng)建一個(gè)合適的ApplicationContext,準(zhǔn)備環(huán)境,并刷新上下文以完成初始化。
2. 環(huán)境準(zhǔn)備
Spring應(yīng)用啟動(dòng)時(shí),需要加載各種配置,例如application.properties或application.yml文件。這些配置決定了應(yīng)用的行為和各種參數(shù)。
ConfigurableApplicationContext context = new SpringApplicationBuilder(DemoApplication.class)
.properties("spring.config.name=application")
.run(args);
在這個(gè)階段,Spring會(huì)根據(jù)環(huán)境(如開(kāi)發(fā)、測(cè)試、生產(chǎn))加載相應(yīng)的配置,并設(shè)置系統(tǒng)屬性和環(huán)境變量。
3. 創(chuàng)建應(yīng)用上下文
ApplicationContext是Spring的核心容器,負(fù)責(zé)管理Bean的生命周期和依賴(lài)關(guān)系。根據(jù)不同的項(xiàng)目需求,Spring可以創(chuàng)建不同類(lèi)型的上下文,如AnnotationConfigApplicationContext用于基于注解的配置。
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(DemoApplication.class);
4. 注冊(cè)Bean定義
這一步驟涉及到掃描指定的包路徑,查找標(biāo)注了@Component、@Service、@Repository等注解的類(lèi),并將它們注冊(cè)為Bean。
例如:
@Component
public class MyService {
// ...
}
Spring通過(guò)ClassPathBeanDefinitionScanner來(lái)掃描這些類(lèi),并將它們的定義保存到BeanFactory中,以備后續(xù)實(shí)例化。
5. 刷新上下文
刷新上下文是整個(gè)啟動(dòng)過(guò)程的關(guān)鍵,涉及到Bean的實(shí)例化、依賴(lài)注入、初始化等操作。
主要步驟包括:
- 實(shí)例化Bean:根據(jù)Bean定義創(chuàng)建Bean實(shí)例。
- 依賴(lài)注入:將需要的依賴(lài)自動(dòng)注入到Bean中。
- 初始化Bean:執(zhí)行Bean的初始化方法,如實(shí)現(xiàn)InitializingBean接口的afterPropertiesSet()方法,或通過(guò)@PostConstruct注解的方法。
- 處理Bean后置處理器:執(zhí)行BeanPostProcessor接口的相關(guān)方法,允許開(kāi)發(fā)者自定義Bean的處理邏輯。
context.refresh();
6. 啟動(dòng)完成
一旦應(yīng)用上下文刷新完成,所有的Bean都已經(jīng)初始化好了,Spring應(yīng)用就正式啟動(dòng)完成。此時(shí),可以開(kāi)始接收HTTP請(qǐng)求、執(zhí)行定時(shí)任務(wù)等操作。
三、示例演示
下面,我們將通過(guò)一個(gè)簡(jiǎn)單的示例來(lái)實(shí)際看看 Spring是如何啟動(dòng)和管理 Bean的。
假設(shè)我們有一個(gè)簡(jiǎn)單的服務(wù)類(lèi)和控制器:
@Component
publicclass GreetingService {
public String greet() {
return"Hello, Spring!";
}
}
@RestController
publicclass GreetingController {
privatefinal GreetingService greetingService;
@Autowired
public GreetingController(GreetingService greetingService) {
this.greetingService = greetingService;
}
@GetMapping("/greet")
public String greet() {
return greetingService.greet();
}
}
在這個(gè)例子中:
- GreetingService被標(biāo)注為@Component,會(huì)被Spring掃描并注冊(cè)為一個(gè)Bean。
- GreetingController通過(guò)構(gòu)造器注入了GreetingService,這就是依賴(lài)注入的體現(xiàn)。
當(dāng)Spring啟動(dòng)時(shí),它會(huì):
- 掃描到GreetingService并創(chuàng)建其實(shí)例。
- 創(chuàng)建GreetingController,并將GreetingService的實(shí)例注入到控制器中。
- 完成所有初始化步驟后,應(yīng)用就可以通過(guò)/greet端點(diǎn)返回問(wèn)候語(yǔ)。
四、總結(jié)
本文,我們分析了 Spring啟動(dòng)的流程,通過(guò)以上的分析和示例,我們可以看到Spring的啟動(dòng)過(guò)程是一個(gè)相對(duì)復(fù)雜但高度自動(dòng)化的過(guò)程。其主要步驟包括引導(dǎo)階段、環(huán)境準(zhǔn)備、創(chuàng)建應(yīng)用上下文、注冊(cè) Bean定義、刷新上下文以及最終的啟動(dòng)完成。每一個(gè)步驟都涉及到大量的底層機(jī)制,例如 Bean的實(shí)例化、依賴(lài)注入、后置處理器等,這些都是 Spring能夠高效管理應(yīng)用的關(guān)鍵。