SpringBoot源码解读-自动装配的原理


一 介绍

既然讲解到了spring关于@Configuration的解析,那就不得不讲一下SpringBoot中的自动装配的原理了。
SpringBoot的自动装配的原理不复杂,实际上就是使用@Import注解,注入一个ImportSelector类型的导入外部配置的核心类AutoConfigurationImportSelector,然后调用ImportSelector中的selectImports(AnnotationMetadata annotationMetadata) 方法[这个实际上各个版本的处理不一样,下面源码分析会讲到],借助于SpringFactoriesLoader的loadFactoryNames方法读取jar包中的META-INF/spring.factories文件,然后匹配符合的类型装配到spring中的容器中

二 源码分析

话不多说,带着过程我们在一点一点的分析Spring Boot的源码实现
首先,SpringBoot中的主函数中,会引入 @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 {
..........
}

SpringBootApplication注解类中,会引入三个主要的注解 @SpringBootConfiguratioin@EnableAutoConfiguratioin@ComponentScan,其中,第一个注解@SpringBootConfiguratioin实际上就是一个@Configuration注解,表明spring boot启动的时候会默认的引导到当前的主函数类视为一个@Configuration类进行解析(前一篇文档讲过)

有一个细节,我们需要关注,SpringBoot在注入ConfigurationClassPostProcessor 解析@Configuration的BeanFactoryPostProcessor的时候,首先是创建ApplicationContext,其实在SpringBoot创建ApplicationContext的时候,是进行了系统环境的判断,来决定使用AnnotationConfigServletWebServerApplicationContext 还是AnnotationConfigReactiveWebServerApplicationContextAnnotationConfigApplicationContext 这三个中的哪个,这一部分的源码是在SpringApplication#createApplicationContext() 方法中,可以自行研究
在这里插入图片描述
第二个注解@EnableAutoConfiguration就是SpringBoot自动装配的核心,@EnableAutoConfiguration注解中,会使用@Import注解注入一个 AutoConfigurationImportSelector 类,这个类是ImportSelector类,

在上一篇文档中Spring源码解读-@Configuration配置等注解源码分析,我们了解到,Spring使用ConfigurationClassPostProcessor进行处理@ImportSelector注解的时候,是会调用ImportSelector类的selectImports(AnnotationMetadata annotationMetadata) 方法,将引导入的装配类以字符串数组的形式返回,然后注入到ioc容器中(这个解析过程是在ConfigurationClassParser#processImports 方法)

/**
	 * @Import 的解析分三种情况
	 * value:
	 * 		1,ImportSelector类型
	 * 		2,ImportBeanDefinitionRegistrar
	 * 		3,普通类
	 * @param configClass
	 * @param currentSourceClass
	 * @param importCandidates
	 * @param checkForCircularImports
	 */
	private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
			Collection<SourceClass> importCandidates, boolean checkForCircularImports) {

		if (importCandidates.isEmpty()) {
			return;
		}

		if (checkForCircularImports && isChainedImportOnStack(configClass)) {
			this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
		}
		else {
			this.importStack.push(configClass);
			try {
				for (SourceClass candidate : importCandidates) {
					if (candidate.isAssignable(ImportSelector.class)) {
						// Candidate class is an ImportSelector -> delegate to it to determine imports
						Class<?> candidateClass = candidate.loadClass();
						ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
						/**
						 * 判断@Import 的resource是否为Aware,加入对应的register environment对象,
						 */
						ParserStrategyUtils.invokeAwareMethods(
								selector, this.environment, this.resourceLoader, this.registry);
						if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {
							this.deferredImportSelectors.add(
									new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
						}
						else {
							String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
							Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
							processImports(configClass, currentSourceClass, importSourceClasses, false);
						}
					}
					else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
						// Candidate class is an ImportBeanDefinitionRegistrar ->
						// delegate to it to register additional bean definitions
						Class<?> candidateClass = candidate.loadClass();
						ImportBeanDefinitionRegistrar registrar =
								BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
						ParserStrategyUtils.invokeAwareMethods(
								registrar, this.environment, this.resourceLoader, this.registry);
						configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
					}
					else {
						// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
						// process it as an @Configuration class
						this.importStack.registerImport(
								currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
						processConfigurationClass(candidate.asConfigClass(configClass));
					}
				}
			}
			catch (BeanDefinitionStoreException ex) {
				throw ex;
			}
			catch (Throwable ex) {
				throw new BeanDefinitionStoreException(
						"Failed to process import candidates for configuration class [" +
						configClass.getMetadata().getClassName() + "]", ex);
			}
			finally {
				this.importStack.pop();
			}
		}
	}

实际上,SpringBoot注入的AutoConfigurationImportSelector实现了DeferredImportSelector接口,在SpringBoot2.0版本之后,DeferredImportSelector 接口引入了一个Group接口,AutoConfigurationImportSelector 接口中实现了这个接口 AutoConfigurationGroup ,而实际的执行是调用到了 DeferredImportSelector 内部接口Group中的process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector)方法, 这个方法在 AutoConfigurationImportSelector 的内部类AutoConfigurationGroup 有实现,这个流程有点复杂,大致的调用路线就是:

ConfigurationClassParser#processImports --> DeferredImportSelectorHandler#handle --> DeferredImportSelectorHandler#processGroupImports --> DeferredImportSelectorGrouping【这个实际就是AutoConfigurationImportSelector内部类AutoConfigurationGroup】#getImports–> AutoConfigurationGroup#process–> AutoConfigurationImportSelector#getAutoConfigurationEntry --> SpringFactoriesLoader#loadFactoryNames

因此,在SpringBoot2.1版本之后,你会发现如果你在AutoConfigurationImportSelector中的selectImports方法中打断点,它死活都不会进去,Spring版本的优化细节还是挺多的

最终,我们进入到了SpringFactoriesLoader中的loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) 方法

首先,先看一下这个方法传递的值是什么
在这里插入图片描述
第一个参数factoryClass传递的是通过getSpringFactoriesLoaderFactoryClass() 方法获取的

protected Class<?> getSpringFactoriesLoaderFactoryClass() {
		return EnableAutoConfiguration.class;
	}

直接返回一个EnableAutoConfiguration的Class字节码对象
第二个参数就是返回当前ClassLoader,最终的解析过程是交由SpringFactoriesLoader的loadSpringFactories进行处理

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

这段代码的核心思路就是通过ClassLoader的getResource()方法,从classpath跟目录下扫描指定目录META-INF/spring.factories下的配置文件,并解析文件内容,找到符合key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的类装配到对应的spring ioc中

三 源码地址

源码地址 源码说明
https://github.com/zcswl7961/spring 基于Spring5
发布了53 篇原创文章 · 获赞 13 · 访问量 2万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 技术黑板 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览