【Springboot】介紹啟動類和啟動過程
- 【一】Spring Boot 啟動類的注解
- 【1】核心注解:@SpringBootApplication
- (1)?@SpringBootConfiguration?:Spring容器會從該類中加載Bean定義
- (2)?@EnableAutoConfiguration?:自動配置依賴
- (3)?@ComponentScan?:?自動掃描并注冊Bean
- 【2】其他常用注解(可根據需要添加)
- 【3】@MapperScan
- 【4】自動裝配原理
- (1)自動裝配的核心思想
- (2)實現原理的核心組件
- 【二】Spring Boot 項目啟動的詳細流程
- 【1】?啟動 Main 方法?
- 【2】?創建 SpringApplication 實例?:
- 【3】?運行 SpringApplication 實例(run方法)??:這是最復雜的核心階段。
- 【三】Spring Boot 生命周期中的擴展點及使用案例
- 【1】Application Events(應用事件) - 觀察者模式
- 【2】ApplicationContextInitializer(應用上下文初始化器)
- 【3】CommandLineRunner / ApplicationRunner
- 【4】Spring Bean 生命周期鉤子
- 【四】Spring Bean 生命周期中的重要組件
- 【1】BeanPostProcessor (BPP) - 容器級后處理器?
- 【2】BeanFactoryPostProcessor (BFPP) - 工廠級后處理器?
- 【3】Aware 接口族 - 感知接口?
- 【4】生命周期回調注解/接口?
- 【5】生命周期執行順序
【一】Spring Boot 啟動類的注解
Spring Boot 啟動類通常就是一個標注了 @SpringBootApplication的類,但這個注解是一個組合注解(Composite Annotation)?,理解它包含的元注解是關鍵。
【1】核心注解:@SpringBootApplication
這是啟動類上唯一必須的注解,它整合了三個核心注解的功能:
(1)?@SpringBootConfiguration?:Spring容器會從該類中加載Bean定義
(1)?作用?:表明當前類是一個配置類。它的底層是 @Configuration,這意味著Spring容器會從該類中加載Bean定義(@Bean注解的方法)。
(2)?為什么不用 @Configuration?:@SpringBootConfiguration是Spring Boot提供的,語義上更明確地指出這是主配置類,但在功能上與 @Configuration無異。
(2)?@EnableAutoConfiguration?:自動配置依賴
(1)?作用?:?啟用Spring Boot的自動配置機制。這是Spring Boot魔法(Convention over Configuration)的核心。
(2)?原理?:這個注解會通知Spring Boot根據你添加的jar包依賴(如classpath下是否存在DataSource、SpringMVC等類),自動猜測并配置你需要的Bean。例如,當你引入了spring-boot-starter-web,它會自動配置Tomcat和Spring MVC。
(3)?@ComponentScan?:?自動掃描并注冊Bean
(1)?作用?:?自動掃描并注冊Bean。默認會掃描啟動類所在包及其所有子包下的所有組件,包括@Component, @Service, @Repository, @Controller等注解的類,并將它們注冊到Spring容器中。
(2)?重要提示?:這是為什么通常要把啟動類放在項目根包(root package)下的原因。如果放在一個很深的包里,@ComponentScan可能無法掃描到其他重要的組件。
【2】其他常用注解(可根據需要添加)
雖然 @SpringBootApplication已經足夠,但在某些場景下,你可能會在啟動類上額外添加一些注解。
(1)?@EnableScheduling?
?作用?:啟用Spring的定時任務功能。添加后,可以使用 @Scheduled注解來創建定時任務。
(2)?@EnableAsync?
?作用?:啟用Spring的異步方法執行功能。添加后,可以使用 @Async注解來標記異步執行的方法。
(3)?@EnableTransactionManagement?
?作用?:啟用注解式事務管理。不過,當Spring Boot檢測到存在事務管理器(如引入了JDBC或JPA starter)時,此功能實際上是默認開啟的。顯式添加此注解只是為了代碼意圖更清晰。
(4)?@EnableCaching?
?作用?:啟用Spring的注解驅動緩存功能。添加后,可以使用 @Cacheable, @CacheEvict等注解。
(5)?@Import?
?作用?:用于導入其他配置類(通常是@Configuration類),這些配置類可能不在@ComponentScan的掃描路徑下。例如:@Import({CustomConfig.class, AnotherConfig.class})。
?總結?:對于大多數標準應用,?只使用 @SpringBootApplication注解就足夠了。其他注解如 @EnableScheduling等,應根據具體功能需求選擇性添加。
【3】@MapperScan
@MapperScan注解的主要目的是告訴 MyBatis 應該去哪個或哪些包路徑下掃描 Mapper 接口,并自動將其注冊為 Spring 容器中的 Bean(MapperFactoryBean)?。
(1)?解決了什么問題???
在沒有這個注解之前,你需要在每個 Mapper 接口上手動添加 @Mapper注解,或者手動配置 MapperFactoryBean,非常繁瑣。@MapperScan通過包掃描的方式,實現了批量、自動的注冊,極大簡化了配置。
(2)?底層機制:??
當 Spring 容器啟動時,它會處理 @MapperScan注解。MyBatis-Spring 整合模塊會為指定的包路徑創建 ClassPathMapperScanner,該掃描器會找到所有接口,并為每個接口動態創建一個 MapperFactoryBean的 BeanDefinition 注冊到 Spring 容器中。MapperFactoryBean是一個 FactoryBean,它的 getObject()方法會使用 SqlSession為原始 Mapper 接口創建一個動態代理實例,并將這個代理實例作為 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"})// 方式三:類型安全的掃描(推薦)
// 創建一個空的標記接口,放在 com.example.mapper 包下
public interface MapperScanMarker {// 無任何方法,僅作為包路徑標記
}@SpringBootApplication
// 掃描標記接口所在的包
@MapperScan(basePackageClasses = MapperScanMarker.class)
public class DemoApplication {// ...
}
【4】自動裝配原理
(1)自動裝配的核心思想
?要解決的問題:?? 傳統 Spring 應用需要大量手動配置(如 XML、Java Config)來集成第三方庫或框架(例如數據源、事務管理器、MVC 等)。這個過程繁瑣且容易出錯。
?解決方案:?? Spring Boot 自動裝配通過預先定義的條件,在檢測到項目的特定依賴、配置和類路徑后,自動為應用注入所需的 Bean 并完成配置。簡單來說,就是 ??“如果我在類路徑上看到了 X,并且你沒有自己配置 Y,那么我就自動給你配置一個默認的 Y”?。
(2)實現原理的核心組件
(1)@SpringBootApplication與 @EnableAutoConfiguration
一切的起點是主啟動類上的 @SpringBootApplication注解。它是一個組合注解?(Composite Annotation),其核心功能之一由 @EnableAutoConfiguration提供。
@SpringBootApplication // 這是一個元注解,整合了其他注解的功能
public class MyApplication {public static void main(String[] args) {SpringApplication.run(MyApplication.class, args);}
}
@EnableAutoConfiguration本身又是一個開關注解,它通過 @Import導入了最核心的加載器:AutoConfigurationImportSelector。
(2)AutoConfigurationImportSelector
這個類是自動裝配的大腦。它的核心任務是決定需要導入哪些自動配置類。
?工作原理?:AutoConfigurationImportSelector會讀取項目 classpath 下所有 JAR 包中的 ?META-INF/spring.factories? 文件。
?關鍵文件?:在 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 需要根據當前應用的環境來智能判斷哪些配置類應該真正生效。這就是 @Conditional系列注解的作用。
這些注解是自動裝配的“開關”,它們會在配置類或 Bean 被加載前進行條件判斷,只有滿足所有條件,配置才會生效。
(4)XXXAutoConfiguration自動配置類
這些是具體的執行者。每個 XXXAutoConfiguration都是一個標準的 ?Spring 配置類?(@Configuration),其內部使用 @Bean方法來定義組件,并且方法上通常裝飾著各種 @Conditional注解。
【二】Spring Boot 項目啟動的詳細流程
Spring Boot 的啟動流程可以看作是傳統 Spring 應用啟動流程的一個高度封裝和自動化版本。其核心是 SpringApplication.run(Application.class, args)方法。
以下是其詳細步驟:
【1】?啟動 Main 方法?
JVM 調用應用程序的 main方法。
【2】?創建 SpringApplication 實例?:
(1)在 run方法內部,首先會創建一個 SpringApplication實例。
(2)這個實例會進行一些初始化工作,例如:
1-?推斷應用類型?:判斷是普通的Servlet應用(Spring MVC)還是響應式Web應用(WebFlux)。
?2-初始化器(Initializers)??:加載 META-INF/spring.factories文件中配置的 ApplicationContextInitializer。
?3-監聽器(Listeners)??:加載 META-INF/spring.factories文件中配置的 ApplicationListener(應用監聽器)。這些監聽器將用于接收整個啟動過程中發布的各種事件。
【3】?運行 SpringApplication 實例(run方法)??:這是最復雜的核心階段。
(1)?a. 啟動計時器 & 發布開始事件?:啟動一個計時器,并發布 ApplicationStartingEvent事件。此時容器還未創建,任何Bean都未初始化。
(2)?b. 準備環境(Environment)??:創建并配置應用運行環境(Environment),讀取所有配置源(application.properties/yaml、系統屬性、環境變量等)。發布 ApplicationEnvironmentPreparedEvent事件。
(3)?c. 創建應用上下文(ApplicationContext)??:根據第一步推斷的應用類型,創建對應的 ApplicationContext(例如,對于Servlet應用,創建 AnnotationConfigServletWebServerApplicationContext)。
(4)?d. 準備應用上下文?:
將環境(Environment)設置到上下文中。
執行所有 ApplicationContextInitializer的 initialize方法,對上下文進行自定義初始化。
發布 ApplicationContextInitializedEvent事件。
(5)?e. 刷新應用上下文(核心中的核心)??:調用上下文的 refresh()方法。這一步完成了傳統Spring應用容器的所有初始化工作:
?加載Bean定義?:解析啟動類(因為它是@Configuration),執行@ComponentScan掃描并注冊所有Bean定義。
?執行自動配置?:執行 @EnableAutoConfiguration邏輯,加載 spring-boot-autoconfigurejar 包中 META-INF/spring.factories文件里的所有自動配置類(XXXAutoConfiguration),根據條件(@ConditionalOnXxx)判斷是否需要配置相應的Bean。
?初始化Bean?:實例化所有非懶加載的單例Bean。
(6)?f. 后置處理?:執行 CommandLineRunner和 ApplicationRunner接口的實現Bean。
(7)?g. 啟動完成?:發布 ApplicationStartedEvent事件,啟動計時器停止,打印啟動耗時日志。
整個流程伴隨著事件的發布,允許開發者通過監聽這些事件在特定階段插入自定義邏輯。
【三】Spring Boot 生命周期中的擴展點及使用案例
Spring Boot 在整個生命周期中提供了大量“鉤子”(Hook),允許開發者介入并執行自定義邏輯。以下是一些最重要的擴展點:
【1】Application Events(應用事件) - 觀察者模式
通過實現 ApplicationListener接口或使用 @EventListener注解來監聽特定事件。
?常用事件?:
ApplicationStartingEvent:應用剛啟動,任何處理都還未進行。
ApplicationEnvironmentPreparedEvent:環境已準備完畢,上下文還未創建。
ApplicationContextInitializedEvent:上下文已創建且初始化器已被調用,但Bean定義還未加載。
ApplicationPreparedEvent:Bean定義已加載,但Bean還未實例化。
ApplicationStartedEvent:上下文已刷新,所有Bean已實例化,CommandLineRunner/ApplicationRunner還未執行。
ApplicationReadyEvent:應用已完全啟動,可以正常接收請求。
?案例:在應用啟動成功后打印一條日志?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!");// 可以在這里執行一些啟動后的檢查,比如檢查數據庫連接狀態等}
}
【2】ApplicationContextInitializer(應用上下文初始化器)
在 ApplicationContext刷新(refresh)之前,對其執行自定義的初始化操作。
?案例:在上下文準備階段設置一個自定義屬性?java
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;public class MyInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {@Overridepublic void initialize(ConfigurableApplicationContext applicationContext) {// 向環境中添加一個屬性applicationContext.getEnvironment().getSystemProperties().put("myCustomProperty", "initialized");}
}
?注冊方式?:需要在 META-INF/spring.factories文件中注冊。
復制org.springframework.context.ApplicationContextInitializer=com.example.MyInitializer
【3】CommandLineRunner / ApplicationRunner
在應用上下文刷新完成后、應用完全啟動之前,執行一些特定的代碼。非常適合進行數據初始化、緩存預熱等操作。兩者功能幾乎一樣,區別在于參數:
CommandLineRunner:提供原始的字符串數組參數 String… args(即main方法的args)。
ApplicationRunner:提供更結構化的 ApplicationArguments對象來解析參數。
?案例:應用啟動后初始化一些數據?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;}@Overridepublic void run(String... args) throws Exception {// 檢查數據庫是否已有數據,如果沒有則插入默認數據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 框架本身的擴展點,在 Spring Boot 中同樣適用。
@PostConstruct:在Bean的依賴注入完成后,初始化方法(InitializingBean.afterPropertiesSet)之前執行。
InitializingBean接口:實現 afterPropertiesSet()方法,在所有屬性設置完成后執行。
@PreDestroy:在Bean被容器銷毀之前執行。
?案例:Bean初始化后連接資源?java
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;@Component
public class ResourceConnector {@PostConstructpublic void connect() {System.out.println("Connecting to external resource...");// 初始化連接}@PreDestroypublic void disconnect() {System.out.println("Disconnecting from external resource...");// 關閉連接,釋放資源}
}
【四】Spring Bean 生命周期中的重要組件
【1】BeanPostProcessor (BPP) - 容器級后處理器?
(1)?作用?:這是Spring框架最強大、最核心的擴展接口。它作用于整個ApplicationContext,對所有Bean的初始化過程進行攔截和增強。你可以把它想象成一個“Bean加工流水線”。
(2)?兩個核心方法?:
1-postProcessBeforeInitialization(Object bean, String beanName):在Bean的初始化回調方法?(如@PostConstruct)之前執行。可以對Bean進行包裝或替換(例如返回一個代理對象,AOP就是基于此實現的)。
2-postProcessAfterInitialization(Object bean, String beanName):在Bean的初始化回調方法之后執行。此時Bean已基本完成初始化。
(3)?重要實現?:AutowiredAnnotationBeanPostProcessor(處理@Autowired注解)、CommonAnnotationBeanPostProcessor(處理@PostConstruct、@Resource等)、AnnotationAwareAspectJAutoProxyCreator(負責AOP動態代理)。
【2】BeanFactoryPostProcessor (BFPP) - 工廠級后處理器?
(1)?作用?:在Bean實例化之前,可以讀取、修改Bean的定義(BeanDefinition)。它操作的是“藍圖”,而不是Bean實例本身。
(2)?核心方法?:postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
(3)?經典案例?:PropertySourcesPlaceholderConfigurer(處理 ${…}占位符)就是在此時將Bean定義中的占位符替換為實際的屬性值。
【3】Aware 接口族 - 感知接口?
?(1)作用?:讓Bean能“感知”到容器本身和某些特定的資源。這些接口的回調發生在BeanPostProcessor之前,初始化方法之后。
(2)?常用接口?:
1-BeanNameAware:感知自己在容器中的Bean名稱。
2-ApplicationContextAware:感知自己所在的ApplicationContext(這是手動獲取Bean的一種方式)。
3-BeanFactoryAware:感知創建自己的BeanFactory。
【4】生命周期回調注解/接口?
(1)?作用?:定義Bean自身初始化和銷毀時的行為。
(2)?初始化?:
?JSR-250注解?:@PostConstruct(推薦使用,標準注解)。
?Spring接口?:InitializingBean及其 afterPropertiesSet()方法。
?XML配置?:init-method屬性。
(3)?銷毀?:
?JSR-250注解?:@PreDestroy(推薦使用)。
?Spring接口?:DisposableBean及其 destroy()方法。
?XML配置?:destroy-method屬性。
【5】生命周期執行順序
?執行順序?:BeanFactoryPostProcessor-> BeanPostProcessor的Before-> Aware接口 -> @PostConstruct-> InitializingBean-> init-method-> BeanPostProcessor的After-> … -> @PreDestroy-> DisposableBean-> destroy-method。