【SpringBoot自动配置原理】

【Java的SPI与SpringBoot自动配置】中我们知道了springBoot的自动配置是参考java的SPI机制的实现。

下面我就一起看下具体的实现流程,到底是如何读取约定的配置文件又是如何自动配制的。

1、读取配置(约定的配置文件)

先来一张流程图, 参照的看会更清晰
请添加图片描述

首先入口大家应该都知道, 就是主启动类上的@SpringBootApplication注解

@SpringBootApplication
public class DemoApplication {
   public static void main(String[] args) {
       SpringApplication.run(DemoApplication.class, args);
   }
}

看源码, 这是一个复合注解, 跟配置有关的有两个注解

  • @SpringBootConfiguration
    作用是将当前启动类(就是上面的DemoApplication)标记为配置类
  • @EnableAutoConfiguration
    无疑, 就是这个注解实现的自动配置了
@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 {
}

继续追踪@EnableAutoConfiguration 注解, 又是一个复合注解

  • @AutoConfigurationPackage
    主要作用是用@Import的方式注入了AutoConfigurationPackages.Registrar类, 这个类主要作用是将启动类的类路径用BasePackages类对象包装了一下(jpa有用到)
  • @Import(AutoConfigurationImportSelector.class)
    这个类实现了DeferredImportSelector和ImportSelector接口, 下面我把核心代码抽出来

如果实现了DeferredImportSelector#getImportGroup方法
就执行AutoConfigurationGroup#process读取配置, 追踪路径如下:
getAutoConfigurationEntry
- getCandidateConfigurations
---- loadFactoryNames
---- loadSpringFactories 从META-INF/spring.factories文件读出所有配置类, 格式为Map<String, LIst< String>>
---- getOrDefault 根据EnableAutoConfiguration.class从map中获取自动配置类
- filter 根据@Conditional进行过滤

否则就会执行ImportSelector#selectImports方法(spring主要走这个逻辑)
读取一批类全限定类名称,去生成bean

(SpringBoot 3.x之后不再读META-INF/spring.factories文件, 改从META-INF/spring/目录下org.springframework.boot.autoconfigure.EnableAutoConfiguration.imports文件中读取)

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

//具体看下AutoConfigurationImportSelector类的实现
public class AutoConfigurationImportSelector implements DeferredImportSelector... {

	//实现ImportSelector方法
	@Override
	public String[] selectImports(...) {
		//这个方法返回的类全限定名的数据, spring会根据这个数组注入bean
		...
	}

	//实现DeferredImportSelector方法
	@Override
	public Class<? extends Group> getImportGroup() {
		return AutoConfigurationGroup.class;
	}
	
	//内部静态类 主要实现DeferredImportSelector.Group类
	private static class AutoConfigurationGroup ...{
		@Override
		public void process(... {
			...
			//读取配置文件
			AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector) .getAutoConfigurationEntry(...);
			...
		}
		@Override
		public Iterable<Entry> selectImports() {
			//排除掉不符合条件配置类, 并排序
		}
	}

	protected AutoConfigurationEntry getAutoConfigurationEntry(...) {
		...
		// 就是在这里参考SPI机制, 读取到所有的配置类信息
		List<String> configurations = getCandidateConfigurations(...);
		...
		// 根据@Conditional进行过滤
		configurations = filter(configurations, autoConfigurationMetadata);
		...
		return new AutoConfigurationEntry(configurations, exclusions);
	}
}

一直追踪代码, 在SpringFactoriesLoader中看到最终读取了META-INF/spring.factories这个文件, 到这里就跟SPI长的很像了~

public final class SpringFactoriesLoader {

	public static List<String> loadFactoryNames(...) {
		String factoryClassName = factoryClass.getName();
		//读取所有配置文件(类)
		Map<String, List<String>> map = loadSpringFactories(classLoader)
		//以EnableAutoConfiguration.class为key从map中获取自动配置类
		return map .getOrDefault(factoryClassName, Collections.emptyList());
	}
	
	private static Map<String, List<String>> loadSpringFactories(...) {
		//先走缓存, 说明之前读过, 就是在SpringApplication.run的时候, new SpringApplication过程中加载初始化器的时候, 有兴趣可以去看看
		MultiValueMap<String, String> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}
		//在这里读了META-INF/spring.factories
		Enumeration<URL> urls = (classLoader != null ?
					classLoader.getResources("META-INF/spring.factories") :
					ClassLoader.getSystemResources("META-INF/spring.factories"));
	}
}

2、自动配制

Spring Boot启动, 先创建SpringApplication然后执行run方法

public static ConfigurableApplicationContext run(...) {
	return new SpringApplication(primarySources).run(args);
}

2.1、创建SpringApplication

可以看到这个时候就已经读取了/META-INF/spring.factories文件, 并把它放到缓存中

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    // 记录主启动类
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    // 应用类型
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    // 设置初始化器(从/META-INF/spring.factories文件中读取)
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    // 设置监听器(从/META-INF/spring.factories文件中读取)
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    // 记录启动类
    this.mainApplicationClass = deduceMainApplicationClass();
}

2.2、执行run方法过程中, 最主要有2个方法prepareContext和refreshContext

prepareContext()主要完成上下文对象初始化操作, 其中最重要的就是load方法, 在这个方法中将启动类封装成Beandefinition注册到registry中, 方便后面进行BeanFactoryPostProcess执行的时候, 完成@SpringBooApplicaion,@EnableAutoConfiguration等注解的解析。

private void prepareContext(...) {
    ...
    // 拿到启动类
    Set<Object> sources = getAllSources();
    Assert.notEmpty(sources, "Sources must not be empty");
    // 将启动类封装成beanDefinition注册到到registry中 annotatedReader.register(source)
    load(context, sources.toArray(new Object[0]));
    ...
}

refreshContext()主要完成容器的刷新过程, 会调用spring的refresh()方法完成应用程序的启动, 这个过程中会调用 invokeBeanFactoryPostProcessors() 方法。
主要是在ConfigurationClassPostProcessor类中进行处理。
这个类是BeanDefinitionRegistryPostProcessor和BeanFactoryPostProcessor的实现类。
执行BeanDefinitionRegistryPostProcessor增强方法时会解析启动类上的所有注解, 比如:@ComponmentScans,@PropertySource,@Import等, 最主要就是**@Import**注解。

public static void invokeBeanFactoryPostProcessors(...) {
	// 执行BeanDefinitionRegistryPostProcessor增强方法
	// 主要就是在ConfigurationClassPostProcessor 类中实现的
	invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
}

public class ConfigurationClassPostProcessor implements ...{

	@Override
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
		...
		processConfigBeanDefinitions(registry);
	}

	public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
		...
		do {
			//在这里进行了解析, candidates中就是启动类
			parser.parse(candidates);
			...
		}while (!candidates.isEmpty());
		...
	}

	public void parse(Set<BeanDefinitionHolder> configCandidates) {
		...
		//在这个方法中执行doProcessConfigurationClass
		parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
		//在这里就跟执行了DeferredImportSelector.Group.process()
		this.deferredImportSelectorHandler.process();
	}

	protected final SourceClass doProcessConfigurationClass(...) throws IOException {
		...
		// 在这里递归解析了 @Import 注解
		processImports(configClass, sourceClass, getImports(sourceClass), true);
	}
}

在解析@Import注解的时候, 从主类开始一直递归解析出所有@Import注解, 此时将@EnableAutoConfiguration中 @Import(AutoConfigurationImportSelect.class)解析出来, 后续在process()方法中调用AutoConfigurationImportSelect类支持的方法(process和selectImports),完成所有META-INF/spring.fatories文件中自动配置类的读取。并将所有符合条件的配置类封装成beanDefinition注册到registry

public class ConfigurationClassPostProcessor implements ...{

	public void parse(Set<BeanDefinitionHolder> configCandidates) {
		...
		//在这里就跟执行了DeferredImportSelector.Group.process()
		this.deferredImportSelectorHandler.process();
	}
	
	public void process() {
		for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
			//遍历所有Import
			grouping.getImports().forEach(entry -> {
				...
			}
		});
	}

	public Iterable<Group.Entry> getImports() {
		for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
			//执行了AutoConfigurationImportSelector.AutoConfigurationGroup.process()
方法
			this.group.process(deferredImport.getConfigurationClass().getMetadata(),
					deferredImport.getImportSelector());
		}
		return this.group.selectImports();
	}
}

private static class AutoConfigurationImportSelector.AutoConfigurationGroup ... {
	@Override
	public void process(...) {
		...
		//这个方法熟悉把度过文章开有那篇文章应该这知道这里边干了啥
		AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector).getAutoConfigurationEntry(getAutoConfigurationMetadata(),annotationMetadata);
		...
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值