SpringBoot自动装配原理分析有图有真相

前面已经手写了starter组件(链接),这次通过源码来分析下自动装配原理

一张图总结

在这里插入图片描述

具体分析

SpringBoot特点之一就是约定大于配置,通过扫描约定目录下的特定文件进行解析,解析完成之后通过自动装配类,将Bean加载到容器中。

SpringBoot启动类很简单,加上@SpringBootApplication,运行SpringApplication.run方法即可。
在这里插入图片描述
1.@SpringBootApplication如下
在这里插入图片描述
图片说明:前四个都是元注解,就不解释了,其余三个
@SpringBootConfiguration :标注当前类是配置类,并会将当前类内声明的一个或多个以@Bean注解标记的方法的实例纳入到spring容器中,并且实例名就是方法名
@ComponentScan::用于自动扫描指定包下的所有组件
@EnableAutoConfiguration:这个注解才是最关键的,先一步步分析下
2.@EnableAutoConfiguration
这个注解最关键的就是导入了AutoConfigurationImportSelector类
在这里插入图片描述
@Import:
(1)如果括号中的类实现了ImportSelector接口,spring容器就会实例化此类,并且调用其selectImports方法。
(2)如果实现了DeferredImportSelector接口,spring容器就会实例化Abc类,并且调用其selectImports方法,DeferredImportSelector是ImportSelector的子类,和ImportSelector的实例不同的是,DeferredImportSelector的实例的selectImports方法调用时机晚于ImportSelector的实例,要等到@Configuration注解中相关的业务全部都处理完了才会调用。
(3)如果实现了ImportBeanDefinitionRegistrar接口,spring容器就会实例化此类,并且调用其registerBeanDefinitions方法;
(4)如果没有实现 ImportSelector、DeferredImportSelector、ImportBeanDefinitionRegistrar等其中的任何一个,spring容器就会实例化此类
3.AutoConfigurationImportSelector类


public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
		ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
//省略其他代码
	@Override
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return NO_IMPORTS;
		}
		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	}
// ...
}

说明:AutoConfigurationImportSelector类DeferredImportSelector接口,会调用selectImports方法,点击方法中getAutoConfigurationEntry方法,重点分析此方法:

protected AutoConfigurationEntry getAutoConfigurationEntry(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 = getConfigurationClassFilter().filter(configurations);
		fireAutoConfigurationImportEvents(configurations, exclusions);
		return new AutoConfigurationEntry(configurations, exclusions);
	}

整体分析:

  • getAttributes:获得@EnableAutoConfiguration注解中的属性,即exclude、excludeName
  • getCandidateConfigurations:扫描classpath下的META-INF/spring.factories,获得所有的自动装配类,具体请看重点分析
  • removeDuplicates:去除重复项
  • getExclusions:根据注解中属性exclude、excludeName得到不需要自动装配的配置类并移除
  • fireAutoConfigurationImportEvents:广播事件,没有研究。

重点分析
getCandidateConfigurations方法

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;
	}

点击SpringFactoriesLoader.loadFactoryNames方法。

public final class SpringFactoriesLoader {
	public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
	//...
	public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
		String factoryTypeName = factoryType.getName();
		return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, 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 factoryTypeName = ((String) entry.getKey()).trim();
					for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
						result.add(factoryTypeName, factoryImplementationName.trim());
					}
				}
			}
			cache.put(classLoader, result);
			return result;
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
	}
//...

说明
SpringFactoriesLoader 是spring内部提供的一种约定俗成的加载方式。
会扫描classpath下的META-INF/spring.factories文件, spring.factories中的数据是以key=value的形式存储,loadFactoryNames方法通过key获取多个value,也就是配置类。
这也就是手写starter组件时在spring.factories加上配置类可以被springboot启动类执行的原因。

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.xiaohou.spring.boot.starter.jdbc.DataSourceAutoConfiguration

总结

  1. 通过@Import(AutoConfigurationImportSelector.class)实现配置类导入,调用selectImports方法批量加载自动配置类
  2. 通过SpringFactoriesLoader机制,扫描classpath下的META-INF/spring.factories文件,读取需要装配的配置类
  3. 通过筛选移除不需要或不符合条件的配置类,完成自动装配。
  • 4
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值