在 Spring Boot 的啟動過程中,BootstrapRegistry
?和?BeanDefinitionRegistry
?是兩個名為“Registry”卻扮演著截然不同角色的核心接口。理解它們的差異是深入掌握 Spring Boot 啟動機制和進行高級定制開發的關鍵。
BootstrapRegistry?
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {return new SpringApplication(primarySources).run(args);}
當我們查看SpringBoot的啟動方法的源碼,我們發現run方法的源碼本質上是初始化了一個SpringApplication對象隨后調用其run方法來執行。我們點擊進去這個SpringApplication的構造方法中我們看到
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {this.resourceLoader = resourceLoader;Assert.notNull(primarySources, "PrimarySources must not be null");this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));this.webApplicationType = WebApplicationType.deduceFromClasspath();this.bootstrapRegistryInitializers = new ArrayList<>(getSpringFactoriesInstances(BootstrapRegistryInitializer.class));setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));this.mainApplicationClass = deduceMainApplicationClass();}
在SpringApplication這個構造方法初始化了兩個類分別是BootstrapRegistryInitializer和ApplicationContextInitializer,這兩個接口都是只有一個方法initialize,他們的作用則是分別對applicationContext和BootstrapRegistry來進行預處理,而BootstrapRegistry的作用則是體現在:當你需要創建一個?ApplicationContextInitializer
?來定制應用上下文,但這個Initializer的邏輯依賴于一個特定的組件(例如,一個高級的?Decryptor
?解密器用來解密數據庫密碼)。然而,這個?Decryptor
?本身又可能依賴于Spring的環境配置(Environment
),而?Environment
?又是在ApplicationContext創建過程中才被完全初始化的。
這就形成了一個?啟動階段的循環依賴:
想創建?
ApplicationContext
?→ 需要先執行?ApplicationContextInitializer
想執行?
ApplicationContextInitializer
?→ 需要先有?Decryptor
?實例想創建?
Decryptor
?實例 → 又需要?Environment
(它又來自于?ApplicationContext
)
BootstrapRegistry
?的解決方案:
BootstrapRegistry
?允許你在一切開始之前(在任何一個?ApplicationContextInitializer
?執行之前),就通過?BootstrapRegistryInitializer
?將像?Decryptor
?這樣的關鍵組件作為完全實例化的對象注冊到一個臨時的“工具箱”里。
因此BootstrapRegistry
?是啟動引導器,負責在最前期提供現成的工具實例。
BeanDefinitionRegistry?
對于BeanDefinitionRegistry?的實例化則是在BeanDefinitionRegistryPostProcessor的處理類當中的postProcessBeanDefinitionRegistry來對其進行實例化處理的。
角色:IoC 容器的建筑師。
定位:它是 Spring Framework 中?
ApplicationContext
?的核心接口,負責持有所有 Bean 的“藍圖”(BeanDefinition
)。ApplicationContext
?會根據這些藍圖來實例化、組裝和管理所有的Bean。它是Spring IoC容器的心臟。它定義了應用程序中所有組件的基本信息,但并不負責實例化,而是為后續的實例化、依賴注入提供元數據。它是正式舞臺的后臺策劃。
也就是說當前容器內的bean所有信息都會放到BeanDefinitionRegistry?中來進行處理,BootstrapRegistry
?的生命周期早于?BeanDefinitionRegistry
。前者為后者的初始化過程提供支持,通常通過?ApplicationContextInitializer
?將?BootstrapRegistry
?中的實例“轉移”或“注冊”到?BeanDefinitionRegistry
?中,使其成為被IoC容器管理的正式Bean。
詳細對比
維度 | BeanDefinitionRegistry | BootstrapRegistry |
---|---|---|
所屬階段 | ApplicationContext 階段?(主階段) | Bootstrap 階段?(極早期) |
核心目的 | 注冊Bean的定義信息?(BeanDefinition ),即如何創建Bean的藍圖。 | 注冊和持有完全實例化的對象實例,為引導過程提供現成的工具。 |
注冊內容 | BeanDefinition ?對象(包含類名、作用域、屬性值、初始化方法等元數據)。 | 任何類型的對象實例?(如?RestTemplate ,?Decryptor )。通過?Supplier ?或?BootstrapRegistry.InstanceSupplier ?延遲提供。 |
生命周期 | 持久。與?ApplicationContext ?同生命周期,伴隨整個應用運行。 | 短暫。僅在 Bootstrap 階段有效。一旦?ApplicationContext ?準備就緒,其使命基本完成,內容可被轉移到正式容器中。 |
接口方法 | registerBeanDefinition(String beanName, BeanDefinition beanDefinition) removeBeanDefinition(String beanName) getBeanDefinition(String beanName) | register(Class<T> type, BootstrapRegistry.InstanceSupplier<T> instanceSupplier) register(Class<T> type, Supplier<T> supplier) isRegistered(Class<T> type) |
依賴解決 | 解決?Bean之間的循環依賴(通過三級緩存機制)。 | 解決?啟動流程上的依賴問題(通過提前實例化關鍵對象,打破初始化順序的僵局)。 |
獲取方式 | 通過?ApplicationContext ?(即?BeanFactory ) 的?getBean() ?方法獲取最終Bean實例。 | 在?ApplicationContextInitializer ?中,通過?BootstrapContext ?的?get(Class<T> type) ?方法獲取預先注冊的實例。 |
典型應用 | 注冊所有由?@Component ,?@Bean ,?@Service ?等注解定義的組件。 | Spring Cloud Config:在獲取遠端配置前注冊安全的?RestTemplate 。自定義引導:提前注冊解密器、自定義的? PropertySourceLoader ?等。 |
設計模式 | 工廠模式:存儲的是產品的設計圖,需要時再根據圖紙生產。 | 倉庫模式:直接存儲了準備好的產品,需要時直接取用。 |
BootstrapRegistry
?和?BeanDefinitionRegistry
?是 Spring Boot 為解耦復雜的啟動過程、清晰劃分職責而設計的兩個互補的組件。它們并非競爭或替代關系,而是分別在不同的生命周期階段、為解決不同層面的問題而協同工作的伙伴。