SpringBoot bean 加载顺序如何查看(源码解读)

Posted amcomputer

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBoot 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配置文件加载顺序

非springboot的注解调用,请问通过啥样的注解可以决定接口的实现类加载顺序?

怎样查看spring容器都有哪些bean

springboot--启动服务自动加载额外配置实现

spring boot importsource怎么设置加载顺序

@Value 和 @Bean 的执行顺序问题