Springboot啟動類和啟動過程超詳細(xì)講解
【一】Spring Boot 啟動類的注解
Spring Boot 啟動類通常就是一個標(biāo)注了 @SpringBootApplication的類,但這個注解是一個組合注解(Composite Annotation)?,理解它包含的元注解是關(guān)鍵。
【1】核心注解:@SpringBootApplication
這是啟動類上唯一必須的注解,它整合了三個核心注解的功能:
(1)?@SpringBootConfiguration?:Spring容器會從該類中加載Bean定義
(1)?作用?:表明當(dāng)前類是一個配置類。它的底層是 @Configuration,這意味著Spring容器會從該類中加載Bean定義(@Bean注解的方法)。
(2)?為什么不用 @Configuration?:@SpringBootConfiguration是Spring Boot提供的,語義上更明確地指出這是主配置類,但在功能上與 @Configuration無異。
(2)?@EnableAutoConfiguration?:自動配置依賴
(1)?作用?:?啟用Spring Boot的自動配置機(jī)制。這是Spring Boot魔法(Convention over Configuration)的核心。
(2)?原理?:這個注解會通知Spring Boot根據(jù)你添加的jar包依賴(如classpath下是否存在DataSource、SpringMVC等類),自動猜測并配置你需要的Bean。例如,當(dāng)你引入了spring-boot-starter-web,它會自動配置Tomcat和Spring MVC。
(3)?@ComponentScan?:?自動掃描并注冊Bean
(1)?作用?:?自動掃描并注冊Bean。默認(rèn)會掃描啟動類所在包及其所有子包下的所有組件,包括@Component, @Service, @Repository, @Controller等注解的類,并將它們注冊到Spring容器中。
(2)?重要提示?:這是為什么通常要把啟動類放在項目根包(root package)下的原因。如果放在一個很深的包里,@ComponentScan可能無法掃描到其他重要的組件。
【2】其他常用注解(可根據(jù)需要添加)
雖然 @SpringBootApplication已經(jīng)足夠,但在某些場景下,你可能會在啟動類上額外添加一些注解。
(1)?@EnableScheduling?
?作用?:啟用Spring的定時任務(wù)功能。添加后,可以使用 @Scheduled注解來創(chuàng)建定時任務(wù)。
(2)?@EnableAsync?
?作用?:啟用Spring的異步方法執(zhí)行功能。添加后,可以使用 @Async注解來標(biāo)記異步執(zhí)行的方法。
(3)?@EnableTransactionManagement?
?作用?:啟用注解式事務(wù)管理。不過,當(dāng)Spring Boot檢測到存在事務(wù)管理器(如引入了JDBC或JPA starter)時,此功能實(shí)際上是默認(rèn)開啟的。顯式添加此注解只是為了代碼意圖更清晰。
(4)?@EnableCaching?
?作用?:啟用Spring的注解驅(qū)動緩存功能。添加后,可以使用 @Cacheable, @CacheEvict等注解。
(5)?@Import?
?作用?:用于導(dǎo)入其他配置類(通常是@Configuration類),這些配置類可能不在@ComponentScan的掃描路徑下。例如:@Import({CustomConfig.class, AnotherConfig.class})。
?總結(jié)?:對于大多數(shù)標(biāo)準(zhǔn)應(yīng)用,?只使用 @SpringBootApplication注解就足夠了。其他注解如 @EnableScheduling等,應(yīng)根據(jù)具體功能需求選擇性添加。
【3】@MapperScan
@MapperScan注解的主要目的是告訴 MyBatis 應(yīng)該去哪個或哪些包路徑下掃描 Mapper 接口,并自動將其注冊為 Spring 容器中的 Bean(MapperFactoryBean)?。
(1)?解決了什么問題???
在沒有這個注解之前,你需要在每個 Mapper 接口上手動添加 @Mapper注解,或者手動配置 MapperFactoryBean,非常繁瑣。@MapperScan通過包掃描的方式,實(shí)現(xiàn)了批量、自動的注冊,極大簡化了配置。
(2)?底層機(jī)制:??
當(dāng) Spring 容器啟動時,它會處理 @MapperScan注解。MyBatis-Spring 整合模塊會為指定的包路徑創(chuàng)建 ClassPathMapperScanner,該掃描器會找到所有接口,并為每個接口動態(tài)創(chuàng)建一個 MapperFactoryBean的 BeanDefinition 注冊到 Spring 容器中。MapperFactoryBean是一個 FactoryBean,它的 getObject()方法會使用 SqlSession為原始 Mapper 接口創(chuàng)建一個動態(tài)代理實(shí)例,并將這個代理實(shí)例作為 Bean 交給 Spring 管理。這就是為什么你可以在 Service 層直接 @Autowired一個接口的原因。
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import @SpringBootApplication
// 方式一:直接掃描一個包
@SpringBootApplication
@MapperScan("com.example.demo.mapper")
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
// 方式二:掃描多個包
@MapperScan(basePackages = {"com.example.mapper", "com.example.other.dao"})
// 方式三:類型安全的掃描(推薦)
// 創(chuàng)建一個空的標(biāo)記接口,放在 com.example.mapper 包下
public interface MapperScanMarker {
// 無任何方法,僅作為包路徑標(biāo)記
}
@SpringBootApplication
// 掃描標(biāo)記接口所在的包
@MapperScan(basePackageClasses = MapperScanMarker.class)
public class DemoApplication {
// ...
}
【4】自動裝配原理
(1)自動裝配的核心思想
?要解決的問題:?? 傳統(tǒng) Spring 應(yīng)用需要大量手動配置(如 XML、Java Config)來集成第三方庫或框架(例如數(shù)據(jù)源、事務(wù)管理器、MVC 等)。這個過程繁瑣且容易出錯。
?解決方案:?? Spring Boot 自動裝配通過預(yù)先定義的條件,在檢測到項目的特定依賴、配置和類路徑后,自動為應(yīng)用注入所需的 Bean 并完成配置。簡單來說,就是 ??“如果我在類路徑上看到了 X,并且你沒有自己配置 Y,那么我就自動給你配置一個默認(rèn)的 Y”?。
(2)實(shí)現(xiàn)原理的核心組件
(1)@SpringBootApplication與 @EnableAutoConfiguration
一切的起點(diǎn)是主啟動類上的 @SpringBootApplication注解。它是一個組合注解?(Composite Annotation),其核心功能之一由 @EnableAutoConfiguration提供。
@SpringBootApplication // 這是一個元注解,整合了其他注解的功能
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
@EnableAutoConfiguration本身又是一個開關(guān)注解,它通過 @Import導(dǎo)入了最核心的加載器:AutoConfigurationImportSelector。
(2)AutoConfigurationImportSelector
這個類是自動裝配的大腦。它的核心任務(wù)是決定需要導(dǎo)入哪些自動配置類。
?工作原理?:AutoConfigurationImportSelector會讀取項目 classpath 下所有 JAR 包中的 ?META-INF/spring.factories? 文件。
?關(guān)鍵文件?:在 spring-boot-autoconfigure-x.x.x.x.jar中,META-INF/spring.factories文件是一個鍵值對形式的配置文件。其中 EnableAutoConfiguration這個 key 后面列出了一長串的自動配置類(XXXAutoConfiguration)。
?spring.factories文件片段示例:??
# Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\ org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\ org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\ org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\ org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\ org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\ org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\ org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\ org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\ org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\ org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\ # ... 省略上百個配置類 org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration
AutoConfigurationImportSelector會獲取所有這些配置類的全限定名,并將它們作為候選配置類。
(3)@Conditional條件注解家族 - ?靈魂所在?
僅僅將候選配置類全部加載是不行的,Spring Boot 需要根據(jù)當(dāng)前應(yīng)用的環(huán)境來智能判斷哪些配置類應(yīng)該真正生效。這就是 @Conditional系列注解的作用。
這些注解是自動裝配的“開關(guān)”,它們會在配置類或 Bean 被加載前進(jìn)行條件判斷,只有滿足所有條件,配置才會生效。
(4)XXXAutoConfiguration自動配置類
這些是具體的執(zhí)行者。每個 XXXAutoConfiguration都是一個標(biāo)準(zhǔn)的 ?Spring 配置類?(@Configuration),其內(nèi)部使用 @Bean方法來定義組件,并且方法上通常裝飾著各種 @Conditional注解。
【二】Spring Boot 項目啟動的詳細(xì)流程
Spring Boot 的啟動流程可以看作是傳統(tǒng) Spring 應(yīng)用啟動流程的一個高度封裝和自動化版本。其核心是 SpringApplication.run(Application.class, args)方法。
以下是其詳細(xì)步驟:
【1】?啟動 Main 方法?
JVM 調(diào)用應(yīng)用程序的 main方法。
【2】?創(chuàng)建 SpringApplication 實(shí)例?:
(1)在 run方法內(nèi)部,首先會創(chuàng)建一個 SpringApplication實(shí)例。
(2)這個實(shí)例會進(jìn)行一些初始化工作,例如:
1-?推斷應(yīng)用類型?:判斷是普通的Servlet應(yīng)用(Spring MVC)還是響應(yīng)式Web應(yīng)用(WebFlux)。
?2-初始化器(Initializers)??:加載 META-INF/spring.factories文件中配置的 ApplicationContextInitializer。
?3-監(jiān)聽器(Listeners)??:加載 META-INF/spring.factories文件中配置的 ApplicationListener(應(yīng)用監(jiān)聽器)。這些監(jiān)聽器將用于接收整個啟動過程中發(fā)布的各種事件。
【3】?運(yùn)行 SpringApplication 實(shí)例(run方法)??:這是最復(fù)雜的核心階段。
(1)?a. 啟動計時器 & 發(fā)布開始事件?:啟動一個計時器,并發(fā)布 ApplicationStartingEvent事件。此時容器還未創(chuàng)建,任何Bean都未初始化。
(2)?b. 準(zhǔn)備環(huán)境(Environment)??:創(chuàng)建并配置應(yīng)用運(yùn)行環(huán)境(Environment),讀取所有配置源(application.properties/yaml、系統(tǒng)屬性、環(huán)境變量等)。發(fā)布 ApplicationEnvironmentPreparedEvent事件。
(3)?c. 創(chuàng)建應(yīng)用上下文(ApplicationContext)??:根據(jù)第一步推斷的應(yīng)用類型,創(chuàng)建對應(yīng)的 ApplicationContext(例如,對于Servlet應(yīng)用,創(chuàng)建 AnnotationConfigServletWebServerApplicationContext)。
(4)?d. 準(zhǔn)備應(yīng)用上下文?:
- 將環(huán)境(Environment)設(shè)置到上下文中。
- 執(zhí)行所有 ApplicationContextInitializer的 initialize方法,對上下文進(jìn)行自定義初始化。
- 發(fā)布 ApplicationContextInitializedEvent事件。
(5)?e. 刷新應(yīng)用上下文(核心中的核心)??:調(diào)用上下文的 refresh()方法。這一步完成了傳統(tǒng)Spring應(yīng)用容器的所有初始化工作:
- ?加載Bean定義?:解析啟動類(因為它是@Configuration),執(zhí)行@ComponentScan掃描并注冊所有Bean定義。
- ?執(zhí)行自動配置?:執(zhí)行 @EnableAutoConfiguration邏輯,加載 spring-boot-autoconfigurejar 包中 META-INF/spring.factories文件里的所有自動配置類(XXXAutoConfiguration),根據(jù)條件(@ConditionalOnXxx)判斷是否需要配置相應(yīng)的Bean。
- ?初始化Bean?:實(shí)例化所有非懶加載的單例Bean。
(6)?f. 后置處理?:執(zhí)行 CommandLineRunner和 ApplicationRunner接口的實(shí)現(xiàn)Bean。
(7)?g. 啟動完成?:發(fā)布 ApplicationStartedEvent事件,啟動計時器停止,打印啟動耗時日志。
整個流程伴隨著事件的發(fā)布,允許開發(fā)者通過監(jiān)聽這些事件在特定階段插入自定義邏輯。
【三】Spring Boot 生命周期中的擴(kuò)展點(diǎn)及使用案例
Spring Boot 在整個生命周期中提供了大量“鉤子”(Hook),允許開發(fā)者介入并執(zhí)行自定義邏輯。以下是一些最重要的擴(kuò)展點(diǎn):
【1】Application Events(應(yīng)用事件) - 觀察者模式
通過實(shí)現(xiàn) ApplicationListener接口或使用 @EventListener注解來監(jiān)聽特定事件。
?常用事件?:
- ApplicationStartingEvent:應(yīng)用剛啟動,任何處理都還未進(jìn)行。
- ApplicationEnvironmentPreparedEvent:環(huán)境已準(zhǔn)備完畢,上下文還未創(chuàng)建。
- ApplicationContextInitializedEvent:上下文已創(chuàng)建且初始化器已被調(diào)用,但Bean定義還未加載。
- ApplicationPreparedEvent:Bean定義已加載,但Bean還未實(shí)例化。
- ApplicationStartedEvent:上下文已刷新,所有Bean已實(shí)例化,CommandLineRunner/ApplicationRunner還未執(zhí)行。
- ApplicationReadyEvent:應(yīng)用已完全啟動,可以正常接收請求。
案例:在應(yīng)用啟動成功后打印一條日志?java
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
public class StartupNotifier {
@EventListener(ApplicationReadyEvent.class)
public void onAppReady() {
System.out.println("?? Application is now ready and can serve traffic!");
// 可以在這里執(zhí)行一些啟動后的檢查,比如檢查數(shù)據(jù)庫連接狀態(tài)等
}
}
【2】ApplicationContextInitializer(應(yīng)用上下文初始化器)
在 ApplicationContext刷新(refresh)之前,對其執(zhí)行自定義的初始化操作。
?案例:在上下文準(zhǔn)備階段設(shè)置一個自定義屬性?java
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
public class MyInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
// 向環(huán)境中添加一個屬性
applicationContext.getEnvironment().getSystemProperties().put("myCustomProperty", "initialized");
}
}
?注冊方式?:需要在 META-INF/spring.factories文件中注冊。
復(fù)制org.springframework.context.ApplicationContextInitializer=com.example.MyInitializer
【3】CommandLineRunner / ApplicationRunner
在應(yīng)用上下文刷新完成后、應(yīng)用完全啟動之前,執(zhí)行一些特定的代碼。非常適合進(jìn)行數(shù)據(jù)初始化、緩存預(yù)熱等操作。兩者功能幾乎一樣,區(qū)別在于參數(shù):
- CommandLineRunner:提供原始的字符串?dāng)?shù)組參數(shù) String… args(即main方法的args)。
- ApplicationRunner:提供更結(jié)構(gòu)化的 ApplicationArguments對象來解析參數(shù)。
?案例:應(yīng)用啟動后初始化一些數(shù)據(jù)?java
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class DataLoader implements CommandLineRunner {
private final UserRepository userRepository;
public DataLoader(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public void run(String... args) throws Exception {
// 檢查數(shù)據(jù)庫是否已有數(shù)據(jù),如果沒有則插入默認(rèn)數(shù)據(jù)
if (userRepository.count() == 0) {
User admin = new User("admin", "admin@example.com");
userRepository.save(admin);
System.out.println("Initial admin user created.");
}
}
}
【4】Spring Bean 生命周期鉤子
這是 Spring 框架本身的擴(kuò)展點(diǎn),在 Spring Boot 中同樣適用。
- @PostConstruct:在Bean的依賴注入完成后,初始化方法(InitializingBean.afterPropertiesSet)之前執(zhí)行。
- InitializingBean接口:實(shí)現(xiàn) afterPropertiesSet()方法,在所有屬性設(shè)置完成后執(zhí)行。
- @PreDestroy:在Bean被容器銷毀之前執(zhí)行。
?案例:Bean初始化后連接資源?java
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@Component
public class ResourceConnector {
@PostConstruct
public void connect() {
System.out.println("Connecting to external resource...");
// 初始化連接
}
@PreDestroy
public void disconnect() {
System.out.println("Disconnecting from external resource...");
// 關(guān)閉連接,釋放資源
}
}
【四】Spring Bean 生命周期中的重要組件
【1】BeanPostProcessor (BPP) - 容器級后處理器?
(1)?作用?:這是Spring框架最強(qiáng)大、最核心的擴(kuò)展接口。它作用于整個ApplicationContext,對所有Bean的初始化過程進(jìn)行攔截和增強(qiáng)。你可以把它想象成一個“Bean加工流水線”。
(2)?兩個核心方法?:
- 1-postProcessBeforeInitialization(Object bean, String beanName):在Bean的初始化回調(diào)方法?(如@PostConstruct)之前執(zhí)行。可以對Bean進(jìn)行包裝或替換(例如返回一個代理對象,AOP就是基于此實(shí)現(xiàn)的)。
- 2-postProcessAfterInitialization(Object bean, String beanName):在Bean的初始化回調(diào)方法之后執(zhí)行。此時Bean已基本完成初始化。
(3)?重要實(shí)現(xiàn)?:AutowiredAnnotationBeanPostProcessor(處理@Autowired注解)、CommonAnnotationBeanPostProcessor(處理@PostConstruct、@Resource等)、AnnotationAwareAspectJAutoProxyCreator(負(fù)責(zé)AOP動態(tài)代理)。
【2】BeanFactoryPostProcessor (BFPP) - 工廠級后處理器?
(1)?作用?:在Bean實(shí)例化之前,可以讀取、修改Bean的定義(BeanDefinition)。它操作的是“藍(lán)圖”,而不是Bean實(shí)例本身。
(2)?核心方法?:postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
(3)?經(jīng)典案例?:PropertySourcesPlaceholderConfigurer(處理 ${…}占位符)就是在此時將Bean定義中的占位符替換為實(shí)際的屬性值。
【3】Aware 接口族 - 感知接口?
?(1)作用?:讓Bean能“感知”到容器本身和某些特定的資源。這些接口的回調(diào)發(fā)生在BeanPostProcessor之前,初始化方法之后。
(2)?常用接口?:
- 1-BeanNameAware:感知自己在容器中的Bean名稱。
- 2-ApplicationContextAware:感知自己所在的ApplicationContext(這是手動獲取Bean的一種方式)。
- 3-BeanFactoryAware:感知創(chuàng)建自己的BeanFactory。
【4】生命周期回調(diào)注解/接口?
(1)?作用?:定義Bean自身初始化和銷毀時的行為。
(2)?初始化?:
- ?JSR-250注解?:@PostConstruct(推薦使用,標(biāo)準(zhǔn)注解)。
- ?Spring接口?:InitializingBean及其 afterPropertiesSet()方法。
- ?XML配置?:init-method屬性。
(3)?銷毀?:
- ?JSR-250注解?:@PreDestroy(推薦使用)。
- ?Spring接口?:DisposableBean及其 destroy()方法。
- ?XML配置?:destroy-method屬性。
【5】生命周期執(zhí)行順序
?執(zhí)行順序?:BeanFactoryPostProcessor-> BeanPostProcessor的Before-> Aware接口 -> @PostConstruct-> InitializingBean-> init-method-> BeanPostProcessor的After-> … -> @PreDestroy-> DisposableBean-> destroy-method。
總結(jié)
到此這篇關(guān)于Springboot啟動類和啟動過程的文章就介紹到這了,更多相關(guān)Springboot啟動類和啟動過程內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot中并發(fā)定時任務(wù)的實(shí)現(xiàn)、動態(tài)定時任務(wù)的實(shí)現(xiàn)(看這一篇就夠了)推薦
這篇文章主要介紹了SpringBoot并發(fā)定時任務(wù)動態(tài)定時任務(wù)實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04
java學(xué)習(xí)之JasperReport踩坑
本篇文章介紹的是在JAVA學(xué)習(xí)中JasperReport遇到的坑以及解決辦法,有需要的朋友參考下吧。2018-01-01
Java使用WeakHashMap實(shí)現(xiàn)緩存自動清理
在 Java 中,內(nèi)存管理是一個重要的話題,尤其是在涉及到緩存的實(shí)現(xiàn)時,如果緩存項不再被使用,我們希望它們能被自動清理,而不必手動刪除,WeakHashMap 就是 Java 提供的一種用于緩存和內(nèi)存管理的工具,本文將深入探討如何利用 WeakHashMap 來實(shí)現(xiàn)緩存自動清理2025-01-01
如何在Springboot實(shí)現(xiàn)攔截器功能
其實(shí)spring boot攔截器的配置方式和springMVC差不多,只有一些小的改變需要注意下就ok了,下面這篇文章主要給大家介紹了關(guān)于如何在Springboot實(shí)現(xiàn)攔截器功能的相關(guān)資料,需要的朋友可以參考下2022-06-06
Java多線程之CAS算法實(shí)現(xiàn)線程安全
這篇文章主要介紹了java中如何通過CAS算法實(shí)現(xiàn)線程安全,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,下面小編和大家一起來學(xué)習(xí)一下吧2019-05-05

