SpringBoot bean加載順序怎樣查看(源碼解讀)
背景
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中,通過<bean id="">方式聲明的bean也是按照代碼的編寫順序依次加載的
(5)同一類中加載順序:Constructor >> @Autowired >> @PostConstruct >> @Bean
(6)同一類中加載順序:靜態(tài)變量 / 靜態(tài)代碼塊 >> 構(gòu)造代碼塊 >> 構(gòu)造方法(需要特別注意的是靜態(tài)代碼塊的執(zhí)行并不是優(yōu)先所有的bean加載,只是在同一個(gè)類中,靜態(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è)類,可以使用下面這個(gè)方式,親測(cè)有效:
beanDefinitionNames.toArray()
后面的bean就不展示順序了。感興趣的讀者可以看自己springBoot項(xiàng)目的。
進(jìn)一步思考
beanDefinitionNames 列表如何來(lái)的呢?
答案是 ConfigurationClassPostProcessor 通過掃描 代碼+注解生成的,講bean 掃描解析成 beanDefinition, 同時(shí)把 bean定義,beanDefinition,注冊(cè)到 BeanDefinitionRegistry, 故有了beanDefinitionNames list。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
java 實(shí)現(xiàn)發(fā)短信功能---騰訊云短信
如今發(fā)短信功能已經(jīng)成為互聯(lián)網(wǎng)公司的標(biāo)配,接下來(lái)通過本文給大家介紹java 實(shí)現(xiàn)發(fā)短信功能---騰訊云短信 ,需要的朋友可以參考下2019-08-08解決Springboot集成Redis集群配置公網(wǎng)IP連接報(bào)私網(wǎng)IP連接失敗問題
在Springboot 集成 Redis集群配置公網(wǎng)IP連接報(bào)私網(wǎng)IP連接失敗,一直報(bào)私有IP連接失敗,所以本文小編給大家介紹了如何解決報(bào)錯(cuò)問題,如果有遇到相同問題的同學(xué),可以參考閱讀本文2023-10-10Java內(nèi)部類持有外部類導(dǎo)致內(nèi)存泄露的原因與解決方案詳解
這篇文章主要為大家詳細(xì)介紹了Java因?yàn)閮?nèi)部類持有外部類導(dǎo)致內(nèi)存泄露的原因以及其解決方案,文中的示例代碼講解詳細(xì),希望對(duì)大家有所幫助2022-11-11解決Tomcat啟動(dòng)報(bào)異常java.lang.ClassNotFoundException問題
這篇文章主要介紹了解決Tomcat啟動(dòng)報(bào)異常java.lang.ClassNotFoundException問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-01-01