Spring Boot 自动装配流程(源码在哪里实现自动装配)

目录

前言 

源码入口

加载主启动类

解析启动类


前言 

关于自动配置原理的文章很多,但一般都是独立的讨论,并没有结合Spring Boot启动流程来讲,现在带着两个疑问看源码(包含@ComponentScan注解和@Import注解的处理等):

  1. Spring Boot源码在哪里读取自动配置的类的?
  2. Spring Boot源码又在哪里把自动配置的类注入到IOC容器的?

源码入口

public ConfigurableApplicationContext run(String... args) {
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		Collection<springbootexceptionreporter> exceptionReporters = new ArrayList&lt;&gt;();
		//设置系统属性『java.awt.headless』,为true则启用headless模式支持
		configureHeadlessProperty();
		//通过*SpringFactoriesLoader*检索*META-INF/spring.factories*,
       //找到声明的所有SpringApplicationRunListener的实现类并将其实例化,
       //之后逐个调用其started()方法,广播SpringBoot要开始执行了
		SpringApplicationRunListeners listeners = getRunListeners(args);
		//发布应用开始启动事件
		listeners.starting();
		try {
		//初始化参数
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
		//创建并配置当前SpringBoot应用将要使用的Environment(包括配置要使用的PropertySource以及Profile),
        //并遍历调用所有的SpringApplicationRunListener的environmentPrepared()方法,广播Environment准备完毕。
			ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
			configureIgnoreBeanInfo(environment);
			//打印banner
			Banner printedBanner = printBanner(environment);
			//创建应用上下文
			context = createApplicationContext();
			//通过*SpringFactoriesLoader*检索*META-INF/spring.factories*,获取并实例化异常分析器
			exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
		//为ApplicationContext加载environment,之后逐个执行ApplicationContextInitializer的initialize()方法来进一步封装ApplicationContext,
        //并调用所有的SpringApplicationRunListener的contextPrepared()方法,【EventPublishingRunListener只提供了一个空的contextPrepared()方法】,
        //之后初始化IoC容器,并调用SpringApplicationRunListener的contextLoaded()方法,广播ApplicationContext的IoC加载完成,
        //这里就包括通过**@EnableAutoConfiguration**导入的各种自动配置类。
			prepareContext(context, environment, listeners, applicationArguments, printedBanner);
			//刷新上下文
			refreshContext(context);
			//再一次刷新上下文,其实是空方法,可能是为了后续扩展。
			afterRefresh(context, applicationArguments);
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
			}
			//发布应用已经启动的事件
			listeners.started(context);
		//遍历所有注册的ApplicationRunner和CommandLineRunner,并执行其run()方法。
        //我们可以实现自己的ApplicationRunner或者CommandLineRunner,来对SpringBoot的启动过程进行扩展。
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}
 
		try {
		//应用已经启动完成的监听事件
			listeners.running(context);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, null);
			throw new IllegalStateException(ex);
		}
		return context;
	}

SpringBoot的启动流程入下图所示,它主要分为加载主启动类和解析启动类两个部分,我将从这两个部分分别开始介绍。

加载主启动类

首先点入SpringApplication.run方法

 之后进入SpringApplication.prepareContext方法

 之后进入SpringApplication.applyInitializers方法

再返回SpringApplication.prepareContext方法进入load(context, sources.toArray(new Object[0]));方法。

 之后进入BeanDefinitionLoader.load方法。

 

 再进入AnnotatedBeanDefinitionReader.register方法并经过AnnotatedBeanDefinitionReader.doRegisterBean最后进入BeanDefinitionReaderUtils.registerBeanDefinition方法,将解析的BeanDefinition注册进入容器。

解析启动类

通过AbstractApplicationContext.refresh方法进入AbstractApplicationContext.invokeBeanFactoryPostProcessors方法


 之后进入PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors方法。

之后进入SharedMetadataReaderFactoryContextInitializer.postProcessBeanDefinitionRegistry方法,这里将自动装配的类放到了容器当中。

 再回到PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors方法,最后转到ConfigurationClassPostProcessor.postProcessBeanFactory方法(注意这里的再回到我在spring2.4.4的源码里是在只是同一方法的后续代码调了这个方法,这里应该也是这个意思,不同版本总体逻辑差不多,细节处有差别,ConfigurationClassPostProcessor类很重要,是java自动配置的核心所在)。

之后进入 ConfigurationClassPostProcessor.processConfigBeanDefinitions方法。

 之后进入ConfigurationClassParser.parse方法

 进入ConfigurationClassParser.processConfigurationClass方法。

 之后进入ConfigurationClassParser.doProcessConfigurationClass方法。

先处理 @ComponentScans注解

再通过ConfigurationClassParser.getImports方法,最后进入ConfigurationClassParser.collectImports进行@Import注解处理

之后通过sourceClass.getAnnotationAttributes(Import.class.getName(), "value")这段代码获取注解上的信息。

回到ConfigurationClassParser.parse方法,改方法最后一行调用了DeferredImportSelectorHandler.process方法

往下走会调用DeferredImportSelectorHandler.processGroupImports方法。

之后进入 DeferredImportSelectorGrouping.getImports方法。

 再通过DeferredImportSelectorGrouping.getImports方法进入AutoConfigurationGroup.process方法,这个方法是自动装配的重点方法。

getAutoConfigurationEntry 方法详细解释一下(@EnableAutoConfiguration 的具体实现类是AutoConfigurationImportSelector,兜了一大圈回到了AutoConfigurationImportSelector#getAutoConfigurationEntry ,整个过程展示了springboot如何一步一步触发自动装配。这通常是我们看源码的一个疑点,某个功能的源码如何实现的我们很容易找到,但是具体在源码里是如何触发的,我们通常没有头绪。

 
/**
 * Return the {@link AutoConfigurationEntry} based on the {@link AnnotationMetadata}
 * of the importing {@link Configuration @Configuration} class.
 * @param annotationMetadata the annotation metadata of the configuration class
 * @return the auto-configurations that should be imported
 */
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
   if (!isEnabled(annotationMetadata)) {
      return EMPTY_ENTRY;
   }
   AnnotationAttributes attributes = getAttributes(annotationMetadata);
   // 通过 SpringFactoriesLoader 类提供的方法加载类路径中的 META-INF 目录下的 
   // spring.factories 文件中针对 EnableAutoConfiguration 的注册配置类
   List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
   // 对获得的注册配置类集合进行去重处理,防止多个项目引入同样的配置类
   configurations = removeDuplicates(configurations);
   // 获得注解中被 exclude 或 excludeName 所排除的类的集合
   Set<String> exclusions = getExclusions(annotationMetadata, attributes);
   // 检查被排除类是否可实例化,是否被自动注册配置所使用,不符合条件则抛出异常
   checkExcludedClasses(configurations, exclusions);
   // 从自动配置类集合中去除被排除的类
   configurations.removeAll(exclusions);
   // 检查配置类的注解是否符合 spring.factories 文件中 AutoConfigurationImportFilter 指定的注解检查条件
   configurations = getConfigurationClassFilter().filter(configurations);
   // 将筛选完成的配置类和排查的配置类构建为事件类,并传入监听器。监听器的配置在于 spring.factories 文件中
   // 通过 AutoConfigurationImportListener 指定
   fireAutoConfigurationImportEvents(configurations, exclusions);
   return new AutoConfigurationEntry(configurations, exclusions);
}

能够发现getCandidateConfigurations 方法中会通过 SpringFactoriesLoader 类来加载类路径中的 META-INF 目录下的 spring.factories 文件中针对 EnableAutoConfiguration 的注册配置类。

到这里所有的自动化配置类都收集了,那是如何组装成BeanDefinition,注册到ioc容器中的呢?我们再回去看org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions

这里看出@ComponetScan和@Import 修饰的类和自配配置的类注册到IOC的时机是不一样的,@ComponetScan在启动类上,它管理的就是启动类之下的子目录所有标注了@Component的bean(@Service这些都是,@Service注解上有@Component),之所以要在底下这个方法注册IOC,看源码是因为@Configuration类有关于条件注解的处理逻辑,在这篇@ConditionalOnBean在方法上失效有相关分析。

org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitions

org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForConfigurationClass

 org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#registerBeanDefinitionForImportedConfigurationClass

下面是Spring Boot 自动装配源码 Debug 主线,感兴趣可以自行跟一下源码。

SpringBoot是如何将自动化配置类加入到ioc容器中的?

SpringBoot源码分析:SpringBoot启动源码(一)

SpringBoot源码分析:SpringBoot自动装配(二)

SpringBoot自动配置实现原理及源码解析(2.3.x)

@Configuration注解源码解析

springboot如何把 bean 加载到 beanDefinitionMap

Spring生命周期详解

————————————————

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.csdn.net/weixin_55229531/article/details/131047561

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值