SpringBoot bean查詢加載順序流程詳解
背景
SpringBoot bean 加載順序如何查看,想看加載了哪些bean, 這些bean的加載順序是什么?
實際加載順序不受控制,但會有一些大的原則:
1、按照字母順序加載(同一文件夾下按照字母數(shù)序;不同文件夾下,先按照文件夾命名的字母順序加載)
2、不同的bean聲明方式不同的加載時機,順序總結(jié):@ComponentScan > @Import > @Bean
這里的ComponentScan指@ComponentScan及其子注解,Bean指的是@configuration + @bean
同時需要注意的是:
(1)Component及其子注解申明的bean是按照字母順序加載的
(2)@configuration + @bean是按照定義的順序依次加載的
(3)@import的順序,就是bean的加載順序
(4)在xml中,通過<bean id="">方式聲明的bean也是按照代碼的編寫順序依次加載的
(5)同一類中加載順序:Constructor >> @Autowired >> @PostConstruct >> @Bean
(6)同一類中加載順序:靜態(tài)變量 / 靜態(tài)代碼塊 >> 構(gòu)造代碼塊 >> 構(gòu)造方法(需要特別注意的是靜態(tài)代碼塊的執(zhí)行并不是優(yōu)先所有的bean加載,只是在同一個類中,靜態(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()
在這里會對 beanDefinitionNames 進行遍歷,然后進行 bean的實例化 和 組裝
因此這里的 beanDefinitionNames 這個列表決定了bean 的 注冊順序。
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這個類,可以使用下面這個方式,親測有效:
beanDefinitionNames.toArray()
后面的bean就不展示順序了。感興趣的讀者可以看自己springBoot項目的。
進一步思考
beanDefinitionNames 列表如何來的呢?
答案是 ConfigurationClassPostProcessor 通過掃描 代碼+注解生成的,講bean 掃描解析成 beanDefinition, 同時把 bean定義,beanDefinition,注冊到 BeanDefinitionRegistry, 故有了beanDefinitionNames list。
到此這篇關(guān)于SpringBoot bean查詢加載順序流程詳解的文章就介紹到這了,更多相關(guān)SpringBoot bean加載順序內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java使用多線程批次查詢大量數(shù)據(jù)(Callable返回數(shù)據(jù))方式
今天給大家分享Java使用多線程批次查詢大量數(shù)據(jù)(Callable返回數(shù)據(jù))方式,多線程有好幾種方式,今天說的方式比較好,實現(xiàn)Callable<> 這種方式能返回查詢的數(shù)據(jù),加上Future異步獲取方式,查詢效率大大加快,感興趣的朋友一起看看吧2023-11-11SpringCloud中的熔斷監(jiān)控HystrixDashboard和Turbine示例詳解
HystrixDashboard是用于實時監(jiān)控Hystrix性能的工具,展示請求響應(yīng)時間和成功率等數(shù)據(jù),本文介紹了如何配置和使用HystrixDashboard和Turbine進行熔斷監(jiān)控,包括依賴添加、啟動類配置和測試流程,感興趣的朋友一起看看吧2024-09-09容器環(huán)境的JVM內(nèi)存設(shè)置實踐記錄
Docker和K8S的興起,很多服務(wù)已經(jīng)運行在容器環(huán)境,對于java程序,JVM設(shè)置是一個重要的環(huán)節(jié),這里總結(jié)下我們項目里的最佳實踐,對容器環(huán)境的JVM內(nèi)存相關(guān)知識感興趣的朋友一起看看吧2022-03-03Spring Security整合KeyCloak保護Rest API實現(xiàn)詳解
這篇文章主要為大家介紹了Spring Security整合KeyCloak保護Rest API實現(xiàn)實例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-11-11Shiro實現(xiàn)session限制登錄數(shù)量踢人下線功能
這篇文章主要介紹了Shiro實現(xiàn)session限制登錄數(shù)量踢人下線,本文記錄的是shiro采用session作為登錄方案時,對用戶進行限制數(shù)量登錄,以及剔除下線,需要的朋友可以參考下2023-11-11解決Idea查看源代碼警告Library source does not mat
在使用IDEA開發(fā)時,遇到第三方j(luò)ar包中的源代碼和字節(jié)碼不一致的問題,會導(dǎo)致無法正確打斷點進行調(diào)試,這通常是因為jar包更新后源代碼沒有同步更新造成的,解決方法是刪除舊的jar包,通過Maven重新下載或手動下載最新的源代碼包,確保IDE中的源碼與字節(jié)碼版本一致2024-10-10