SpringBoot自动配置分析(一)

本文深入剖析了SpringBoot的自动配置机制,详细解读了@SpringBootApplication、@EnableAutoConfiguration等注解的功能,以及它们如何引导自动配置类的加载过程,揭示了SpringBoot如何智能地配置应用程序。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

SpringBoot自动配置分析

在这里插入图片描述
@SpringBootApplication注解,这是SpringBoot应用运行开始的地方。如果去掉该注解,程序无法运行。点进该注解看看有什么东西。按住ctrt鼠标左键单击@SpringBootApplication。
在这里插入图片描述
先来看看@EnableAutoConfiguratiion注解。顾名思义启用自动配置,这正是我们想要的东西。继续点进去看。
在这里插入图片描述
又出现了一大堆的注解,不要紧我们只看我们想要的。@AutoConfigurationPackage 直译过来是自动配置包。这是个什么玩意?点进去看源码。
在这里插入图片描述
@Import注解导入资源,可以看到导入的是Registrar这个内部类的字节码。点进Registrar这个内部类看看源码。

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

		@Override
		public void registerBeanDefinitions(AnnotationMetadata metadata,
				BeanDefinitionRegistry registry) {
			register(registry, new PackageImport(metadata).getPackageName());
		}

目光停留在getPackageName()上,获取包名?打个断点,debug一下。
在这里插入图片描述
这样看不是很明白,结合我的项目的目录树看看就明白了。
在这里插入图片描述
原来所谓的包扫描就是把springboot应用的启动类所在的包下所有的类扫描一遍。遇到@bean之类的就把它加入到容器中。

再看看@EnableAutoconfigura注解里的@Import(AutoConfigurationImportSelector.class)。直译过来自动导入选择器。点进这个类看看。

protected AutoConfigurationEntry getAutoConfigurationEntry(
		AutoConfigurationMetadata autoConfigurationMetadata,
		AnnotationMetadata annotationMetadata) {
	if (!isEnabled(annotationMetadata)) {
		return EMPTY_ENTRY;
	}
	AnnotationAttributes attributes = getAttributes(annotationMetadata);
	List<String> configurations = getCandidateConfigurations(annotationMetadata,
			attributes);
	configurations = removeDuplicates(configurations);
	Set<String> exclusions = getExclusions(annotationMetadata, attributes);
	checkExcludedClasses(configurations, exclusions);
	configurations.removeAll(exclusions);
	configurations = filter(configurations, autoConfigurationMetadata);
	fireAutoConfigurationImportEvents(configurations, exclusions);
	return new AutoConfigurationEntry(configurations, exclusions);
}

这个方法名叫获取自动配置实体,打几个断点debug一下。
在这里插入图片描述
可以看到configurations保存了一大堆自动配置类的名称。那么这些东西是从哪里来的呢?继续翻这个类的源码。可以看到confiurations通过getCandidateConfigurations(annotationMetadata,attributes)这个方法获取值的。

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
			AnnotationAttributes attributes) {
		List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
				getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
		Assert.notEmpty(configurations,
				"No auto configuration classes found in META-INF/spring.factories. If you "
						+ "are using a custom packaging, make sure that file is correct.");
		return configurations;
	}

点进loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());这个方法。这个方法牵扯到两个方法。

public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
	String factoryClassName = factoryClass.getName();
	return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}

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 factoryClassName = ((String) entry.getKey()).trim();
				for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
					result.add(factoryClassName, factoryName.trim());
				}
			}
		}
		cache.put(classLoader, result);
		return result;
	}
	catch (IOException ex) {
		throw new IllegalArgumentException("Unable to load factories from location [" +
				FACTORIES_RESOURCE_LOCATION + "]", ex);
	}
}

目光再次停留在FACTORIES_RESOURCE_LOCATION这个常量上。点进去看看到底是什么。

public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

结合上面的代码,原来loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());的作用是扫描所有jar包下的"META-INF/spring.factories"文件。所谓spring.factories文件就是一系列的键值对。读取每一个spring.factories把键值存放在Properties对象中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值