SpringBoot bean查詢(xún)加載順序流程詳解
背景
SpringBoot bean 加載順序如何查看,想看加載了哪些bean, 這些bean的加載順序是什么?
實(shí)際加載順序不受控制,但會(huì)有一些大的原則:
1、按照字母順序加載(同一文件夾下按照字母數(shù)序;不同文件夾下,先按照文件夾命名的字母順序加載)
2、不同的bean聲明方式不同的加載時(shí)機(jī),順序總結(jié):@ComponentScan > @Import > @Bean
這里的ComponentScan指@ComponentScan及其子注解,Bean指的是@configuration + @bean
同時(shí)需要注意的是:
(1)Component及其子注解申明的bean是按照字母順序加載的
(2)@configuration + @bean是按照定義的順序依次加載的
(3)@import的順序,就是bean的加載順序
(4)在xml中,通過(guò)<bean id="">方式聲明的bean也是按照代碼的編寫(xiě)順序依次加載的
(5)同一類(lèi)中加載順序:Constructor >> @Autowired >> @PostConstruct >> @Bean
(6)同一類(lèi)中加載順序:靜態(tài)變量 / 靜態(tài)代碼塊 >> 構(gòu)造代碼塊 >> 構(gòu)造方法(需要特別注意的是靜態(tài)代碼塊的執(zhí)行并不是優(yōu)先所有的bean加載,只是在同一個(gè)類(lèi)中,靜態(tài)代碼塊優(yōu)先加載)
探索-源碼
入口:
public class TestApplication {
public static void main(String[] args) {
try {
SpringApplication.run(TestApplication.class, args);
LOGGER.info("SpringBoot Application Start!!!");
} catch (Throwable e) {
throw e;
}
}
}
其中 里面的run方法為:
public ConfigurableApplicationContext run(String... args) {
long startTime = System.nanoTime();
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
**refreshContext**(context);
afterRefresh(context, applicationArguments);
Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
}
listeners.started(context, timeTakenToStartup);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
try {
Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
listeners.ready(context, timeTakenToReady);
}
catch (Throwable ex) {
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
return context;
}
refreshContext(context);
private void refreshContext(ConfigurableApplicationContext context) {
if (this.registerShutdownHook) {
shutdownHook.registerApplicationContext(context);
}
**refresh**(context);
}
AbstractApplicationContext#refresh
然后看倒數(shù)第二行:finishBeanFactoryInitialization(beanFactory);
org.springframework.context.support.AbstractApplicationContext#refresh
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
**finishBeanFactoryInitialization(beanFactory);**
// Last step: publish corresponding event.
finishRefresh();
}
finishBeanFactoryInitialization(beanFactory)
然后看最后一行:beanFactory.preInstantiateSingletons();
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// Initialize conversion service for this context.
if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
beanFactory.setConversionService(
beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
}
// Register a default embedded value resolver if no BeanFactoryPostProcessor
// (such as a PropertySourcesPlaceholderConfigurer bean) registered any before:
// at this point, primarily for resolution in annotation attribute values.
if (!beanFactory.hasEmbeddedValueResolver()) {
beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
}
// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
for (String weaverAwareName : weaverAwareNames) {
getBean(weaverAwareName);
}
// Stop using the temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(null);
// Allow for caching all bean definition metadata, not expecting further changes.
beanFactory.freezeConfiguration();
// Instantiate all remaining (non-lazy-init) singletons.
**beanFactory.preInstantiateSingletons();**
}
beanFactory.preInstantiateSingletons()
在這里會(huì)對(duì) beanDefinitionNames 進(jìn)行遍歷,然后進(jìn)行 bean的實(shí)例化 和 組裝
因此這里的 beanDefinitionNames 這個(gè)列表決定了bean 的 注冊(cè)順序。
org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons
@Override
public void preInstantiateSingletons() throws BeansException {
if (logger.isTraceEnabled()) {
logger.trace("Pre-instantiating singletons in " + this);
}
// Iterate over a copy to allow for init methods which in turn register new bean definitions.
// While this may not be part of the regular factory bootstrap, it does otherwise work fine.
**List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);**
// Trigger initialization of all non-lazy singleton beans...
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
if (isFactoryBean(beanName)) {
Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
if (bean instanceof FactoryBean) {
FactoryBean<?> factory = (FactoryBean<?>) bean;
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged(
(PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit,
getAccessControlContext());
}
else {
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
if (isEagerInit) {
getBean(beanName);
}
}
}
else {
getBean(beanName);
}
}
}
// Trigger post-initialization callback for all applicable beans...
for (String beanName : beanNames) {
Object singletonInstance = getSingleton(beanName);
if (singletonInstance instanceof SmartInitializingSingleton) {
StartupStep smartInitialize = this.getApplicationStartup().start("spring.beans.smart-initialize")
.tag("beanName", beanName);
SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
smartSingleton.afterSingletonsInstantiated();
return null;
}, getAccessControlContext());
}
else {
smartSingleton.afterSingletonsInstantiated();
}
smartInitialize.end();
}
}
}

如果不能看,像圖中一樣,不能找到j(luò)ava.util.list這個(gè)類(lèi),可以使用下面這個(gè)方式,親測(cè)有效:
beanDefinitionNames.toArray()

后面的bean就不展示順序了。感興趣的讀者可以看自己springBoot項(xiàng)目的。
進(jìn)一步思考
beanDefinitionNames 列表如何來(lái)的呢?
答案是 ConfigurationClassPostProcessor 通過(guò)掃描 代碼+注解生成的,講bean 掃描解析成 beanDefinition, 同時(shí)把 bean定義,beanDefinition,注冊(cè)到 BeanDefinitionRegistry, 故有了beanDefinitionNames list。
到此這篇關(guān)于SpringBoot bean查詢(xún)加載順序流程詳解的文章就介紹到這了,更多相關(guān)SpringBoot bean加載順序內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java數(shù)據(jù)結(jié)構(gòu)常見(jiàn)幾大排序梳理
Java常見(jiàn)的排序算法有:直接插入排序、希爾排序、選擇排序、冒泡排序、歸并排序、快速排序、堆排序等。本文詳解介紹它們的實(shí)現(xiàn)以及圖解,需要的可以參考一下2022-03-03
Java開(kāi)發(fā)學(xué)習(xí) Eclipse項(xiàng)目有紅感嘆號(hào)解決之道
這篇文章主要為大家詳細(xì)介紹了完美解決Eclipse項(xiàng)目有紅感嘆號(hào)問(wèn)題的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-04-04
詳解關(guān)于Windows10 Java環(huán)境變量配置問(wèn)題的解決辦法
這篇文章主要介紹了關(guān)于Windows10 Java環(huán)境變量配置問(wèn)題的解決辦法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-03-03
spring?boot?validation參數(shù)校驗(yàn)與分組嵌套各種類(lèi)型及使用小結(jié)
參數(shù)校驗(yàn)基本上是controller必做的事情,畢竟前端傳過(guò)來(lái)的一切都不可信,validation可以簡(jiǎn)化這一操作,這篇文章主要介紹了spring?boot?validation參數(shù)校驗(yàn)分組嵌套各種類(lèi)型及使用小結(jié),需要的朋友可以參考下2023-09-09
基于SpringBoot實(shí)現(xiàn)離線(xiàn)應(yīng)用的4種實(shí)現(xiàn)方式
在當(dāng)今高度依賴(lài)網(wǎng)絡(luò)的環(huán)境中,離線(xiàn)應(yīng)用的價(jià)值日益凸顯,而且具備離線(xiàn)工作能力已成為許多應(yīng)用的必備特性,本文將介紹基于SpringBoot實(shí)現(xiàn)離線(xiàn)應(yīng)用的4種不同方式2025-06-06
淺談Java后臺(tái)對(duì)JSON格式的處理操作
下面小編就為大家?guī)?lái)一篇淺談Java后臺(tái)對(duì)JSON格式的處理操作。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-06-06
EVCache緩存在Spring Boot中的實(shí)戰(zhàn)示例
這篇文章主要介紹了EVCache緩存在Spring Boot中的實(shí)戰(zhàn)示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-12-12

