SpringBoot bean查询加载顺序流程详解

 更新时间:2023年03月12日 11:34:45   作者:amcomputer  
当你在项目启动时需要提前做一个业务的初始化工作时,或者你正在开发某个中间件需要完成自动装配时。你会声明自己的Configuration类,但是可能你面对的是好几个有互相依赖的Bean

背景

SpringBoot bean 加载顺序如何查看,想看加载了哪些bean, 这些bean的加载顺序是什么?

实际加载顺序不受控制,但会有一些大的原则:

1、按照字母顺序加载(同一文件夹下按照字母数序;不同文件夹下,先按照文件夹命名的字母顺序加载)
2、不同的bean声明方式不同的加载时机,顺序总结:@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)同一类中加载顺序:静态变量 / 静态代码块 >> 构造代码块 >> 构造方法(需要特别注意的是静态代码块的执行并不是优先所有的bean加载,只是在同一个类中,静态代码块优先加载)

探索-源码

入口:

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

然后看倒数第二行: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();
			}
		}
	}

如果不能看,像图中一样,不能找到java.util.list这个类,可以使用下面这个方式,亲测有效:

beanDefinitionNames.toArray()

后面的bean就不展示顺序了。感兴趣的读者可以看自己springBoot项目的。

进一步思考

beanDefinitionNames 列表如何来的呢?

答案是 ConfigurationClassPostProcessor 通过扫描 代码+注解生成的,讲bean 扫描解析成 beanDefinition, 同时把 bean定义,beanDefinition,注册到 BeanDefinitionRegistry, 故有了beanDefinitionNames list。

到此这篇关于SpringBoot bean查询加载顺序流程详解的文章就介绍到这了,更多相关SpringBoot bean加载顺序内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java使用多线程批次查询大量数据(Callable返回数据)方式

    Java使用多线程批次查询大量数据(Callable返回数据)方式

    今天给大家分享Java使用多线程批次查询大量数据(Callable返回数据)方式,多线程有好几种方式,今天说的方式比较好,实现Callable<> 这种方式能返回查询的数据,加上Future异步获取方式,查询效率大大加快,感兴趣的朋友一起看看吧
    2023-11-11
  • Java编程之文件读写实例详解

    Java编程之文件读写实例详解

    这篇文章主要介绍了Java编程之文件读写的方法,结合实例形式较为详细的分析了Java文件读写所涉及的类及相应的操作技巧,需要的朋友可以参考下
    2015-12-12
  • SpringCloud中的熔断监控HystrixDashboard和Turbine示例详解

    SpringCloud中的熔断监控HystrixDashboard和Turbine示例详解

    HystrixDashboard是用于实时监控Hystrix性能的工具,展示请求响应时间和成功率等数据,本文介绍了如何配置和使用HystrixDashboard和Turbine进行熔断监控,包括依赖添加、启动类配置和测试流程,感兴趣的朋友一起看看吧
    2024-09-09
  • 容器环境的JVM内存设置实践记录

    容器环境的JVM内存设置实践记录

    Docker和K8S的兴起,很多服务已经运行在容器环境,对于java程序,JVM设置是一个重要的环节,这里总结下我们项目里的最佳实践,对容器环境的JVM内存相关知识感兴趣的朋友一起看看吧
    2022-03-03
  • 详解Java的MyBatis框架中的缓存与缓存的使用改进

    详解Java的MyBatis框架中的缓存与缓存的使用改进

    很多人在使用MyBatis的缓存后经常会遇到MySQL分页查询的显示问题,针对于此,这里我们就来详解Java的MyBatis框架中的缓存与缓存的使用改进,首先来回顾一下MyBatis的缓存机制与执行:
    2016-06-06
  • Spring Security整合KeyCloak保护Rest API实现详解

    Spring Security整合KeyCloak保护Rest API实现详解

    这篇文章主要为大家介绍了Spring Security整合KeyCloak保护Rest API实现实例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-11-11
  • SpringBoot整合JWT实战教程

    SpringBoot整合JWT实战教程

    JWT(JSON Web Token)是一种用于身份验证和授权的开放标准(RFC 7519),它使用JSON格式传输信息,可以在不同系统之间安全地传递数据,这篇文章主要介绍了SpringBoot整合JWT实战教程,需要的朋友可以参考下
    2023-06-06
  • Shiro实现session限制登录数量踢人下线功能

    Shiro实现session限制登录数量踢人下线功能

    这篇文章主要介绍了Shiro实现session限制登录数量踢人下线,本文记录的是shiro采用session作为登录方案时,对用户进行限制数量登录,以及剔除下线,需要的朋友可以参考下
    2023-11-11
  • java 排序算法之归并排序

    java 排序算法之归并排序

    本文主要讲解了排序算法中的归并排序,文中运用大量的图片和代码讲解的非常详细,感兴趣的朋友可以学习一下这篇文章,相信可以帮助到你
    2021-09-09
  • 解决Idea查看源代码警告Library source does not match the bytecode for class XXX问题

    解决Idea查看源代码警告Library source does not mat

    在使用IDEA开发时,遇到第三方jar包中的源代码和字节码不一致的问题,会导致无法正确打断点进行调试,这通常是因为jar包更新后源代码没有同步更新造成的,解决方法是删除旧的jar包,通过Maven重新下载或手动下载最新的源代码包,确保IDE中的源码与字节码版本一致
    2024-10-10

最新评论