@EnableAutoConfiguration的使用和原理

23 篇文章 0 订阅
6 篇文章 0 订阅

Springboot是如何加载@EnableAutoConfiguration

本文着重介绍,如何让用户自定义的类通过@EnableAutoConfiguration加载

1. 如何把自己的配置类加入到Spring容器中呢?

1.1 spring.factories

此处代码,我们可以借助开源框架的mybatis-plus来理解
在这里插入图片描述

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration

可以发现此处直接把自己想要加入到spring容器中的配置类作为org.springframework.boot.autoconfigure.EnableAutoConfiguration的value就行,是不是很简单。那么Springboot是如何基于这种配置加载我们的配置类的呢?

2. 探究Springboot是如何加载我们的配置类的

首先,让我们来看看Springboot的几个基础注解

2.1 注解介绍

2.1.1 @SpringbootApplication注解是什么
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

可以看到该注解继承了@SpringbootConfiguration注解和@EnableAutoConfiguration注解

2.1.2 @SpringbootConfiguration注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {

此处可以看到@SpringbootConfiguration注解是@Configuration注解的子类

2.1.3 @EnableAutoConfiguration注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

熟悉Springboot的@EnableXXX相关注解的同学,看到@Import注解就开心了~

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
		ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {

不过需要注意的是此处AutoConfigurationImportSelector类实现的DeferredImportSelector而不同于一般的ImportSelector

通过对以上三个注解的,以及注解之间关系的介绍,大家可以把加了@SpringbootApplication注解的启动类认为是加了**@ComponentScan、@Configuration、@Import**注解的类

2.2 Springboot启动过程解析spring.factories

在这里插入图片描述
Springboot在启动过程中,会在这个方法中,加载所有的spring.factories里面的配置,并缓存起来。

SpringFactoriesLoader类
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
		MultiValueMap<String, String> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}

		try {
			Enumeration<URL> urls = (classLoader != null ?
					classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
					ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
			result = new LinkedMultiValueMap<>();
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				UrlResource resource = new UrlResource(url);
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				for (Map.Entry<?, ?> entry : properties.entrySet()) {
					String factoryTypeName = ((String) entry.getKey()).trim();
					for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
						result.add(factoryTypeName, factoryImplementationName.trim());
					}
				}
			}
			//把classLoader作为key,将spring.factories里面的配置保存起来,cache是静态常量
			cache.put(classLoader, result);
			return result;
		}

在这里插入图片描述
通过断点可以看到运行情况

2.3 Spring容器初始化过程中的处理@EnableAutoConfiguration注解

2.3.1 ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry方法执行过程

此处涉及到Spring容器初始化过程,以后再记录。先简单记录下:
Spring容器初始化过程中,会调用ConfigurationClassPostProcessorpostProcessBeanDefinitionRegistry方法进行BeanDefinition的注册,此处简单记录下调用链:

postProcessBeanDefinitionRegistry -> 
processConfigBeanDefinitions -> 
ConfigurationClassParser#parse(这个方法牛逼) ->
ConfigurationClassParser#validate ->
ConfigurationClassBeanDefinitionReader#loadBeanDefinitions
2.3.2 ConfigurationClassParser#parse
ConfigurationClassParser#parse(这个方法牛逼) ->  {
	ConfigurationClassParser#processConfigurationClass -> {
		ConfigurationClassParser#doProcessConfigurationClass -> {
			1. 通过componentScanParser.parse来处理类上面的@ComponentScan和@ComponentScans
			2. processImports方法处理所有的@Import注解
			3. 处理@ImportResource注解
			4. 处理@Bean注解
		}
		this.configurationClasses.put(configClass, configClass);这一步很重要,很多开源框架都是通过调用此处,将类变为bd,注入的。
	}
	//注意这一步,也就是ConfigurationClassParser#parse方法的最后一步
	this.deferredImportSelectorHandler.process();
}
2.3.3 ConfigurationClassParser#processImports执行过程

今天重点关注processImports方法,拿到所有的@import注解里import进来的类:

1. 如果是DeferredImportSelector实现类:封装之后放入到ConfigurationClassParser.deferredImportSelectors中
2. 如果是ImportSelector实现类:调用接口的selectImports方法,对返回值的classNames进行processImports,递归循环起来
3. 如果是ImportBeanDefinitionRegistrar:实例化之后放入到ConfigurationClass.importBeanDefinitionRegistrars中,后续处理
4. 如果不满足上述三者,则对import进来的类进行ConfigurationClassParser#processConfigurationClass操作,这样就进入了循环递归,将import进来的配置类进行处理,同时在处理的过程中,会把配置类放入到ConfigurationClassParser.configurationClasses中,这一步很重要,很多开源框架都是通过调用此处,将类变为bd,注入的。
2.3.4 deferredImportSelectorHandler#process

由于上文介绍过AutoConfigurationImportSelector是DeferredImportSelector,所以放入到ConfigurationClassParser.deferredImportSelectors后续处理;

回到ConfigurationClassParser#parse中,当上述操作执行完了之后,进行

this.deferredImportSelectorHandler.process() -> {
	1. DeferredImportSelectorGroupingHandler#register -> {
		1.1. 调用了getImportGroup方法,获取class,生成对应的group。注意!!!AutoConfigurationImportSelector类重写了getImportGroup,返回了其内部类AutoConfigurationGroup
		}
	2. DeferredImportSelectorGroupingHandler#processGroupImports-> { 
		2.1. 调用group的process方法,
		也就是调用了AutoConfigurationGroup#process -> 
		getAutoConfigurationEntry -> 
		getCandidateConfigurations -> 
		SpringFactoriesLoader.loadFactoryNames 终于拿到了在2.2节放入到cache中的ClassName,拿到了以后你猜猜又干嘛了。对嘛,又调用了processImports方法。}
}
2.3.5 this.reader.loadBeanDefinitions(configClasses)

上述执行完成之后,ConfigurationClassPostProcessor#processConfigBeanDefinitions中,执行this.reader.loadBeanDefinitions(configClasses);此处会把ConfigurationClassParser.configurationClasses的keySet全都new成BeanDefinition放入到容器中,同时beanDefinitionNames中放入bd的名字,还要同时处理ImportResource注解内容,以及processImports的第3步的后续。

2.3.6 补充一个方法调用时序图

在这里插入图片描述

之后就是Spring容器根据bd创建bean的过程了。

完~

  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
@EnableAutoConfigurationSpring Boot的一个注解,它的作用是启用Spring应用程序上下文中的自动配置机制。在Spring Boot应用中,开发人员可以通过引入不同的starter依赖来快速地构应用,而@EnableAutoConfiguration注解则可以根据当前应用的依赖和配置情况,自动为应用添加必要的配置。 @EnableAutoConfiguration实现的原理主要涉及到Spring Boot自动配置机制中的以下两个关键组件: 1. Spring Boot的自动配置 Spring Boot中的自动配置是通过@Configuration注解来声明的。这些自动配置包含了应用中可能需要的各种配置,包括数据库连接、事务管理、Web MVC、缓存等。在启动应用时,Spring Boot会扫描应用中的所有,如果检测到某个自动配置,则会将其加载到Spring容器中,从而实现自动配置。 2. Spring Boot的条件注解 Spring Boot中的条件注解是通过@Conditional注解实现的。这些条件注解可以根据应用的配置情况,判断是否需要加载某个自动配置。例如,如果应用中没有配置数据源,则不会加载与数据源相关的自动配置。 @EnableAutoConfiguration注解的作用就是将Spring Boot自动配置机制中的自动配置和条件注解结合起来,从而实现自动配置。在启动应用时,Spring Boot会根据当前应用的依赖和配置情况,扫描classpath中的所有,并根据条件注解来判断是否需要加载某个自动配置。最终,Spring Boot会将所有需要加载的自动配置加载到Spring容器中,完成应用的自动配置。 总之,@EnableAutoConfiguration注解的原理是基于Spring Boot自动配置机制中的自动配置和条件注解,通过扫描应用中的所有,根据应用的依赖和配置情况,自动为应用添加必要的配置。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值