SpringBoot源码解析 -- AutoConfigure的实现原理

326 阅读6分钟

SpringBoot深入理解 -- @AliasFor注解的作用
SpringBoot源码解析 -- SpringBoot启动过程
SpringBoot源码解析 -- AutoConfigure的实现原理
SpringBoot源码解析 -- @ComponentScan的实现原理
SpringBoot源码解析 -- @Value,@Autowired实现原理
SpringBoot源码解析 -- Tomcat,SpringMVC启动
SpringBoot源码解析 -- Logging,Environment启动

源码分析基于spring boot 2.1

SpringBoot中使用@EnableAutoConfiguration注解启动AutoConfigure功能

@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
	...
}

这里起作用的实际上是@Import和AutoConfigurationImportSelector。
@Import注解非常重要,它是SpringBoot中AutoConfiguration功能的基础。

前面解析SpringBoot启动过程的文章说过,SpringBoot启动时会注入ConfigurationClassPostProcessor,该PostProcessor正是处理@Import的类。

ConfigurationClassPostProcessor#postProcessBeanFactory -> ConfigurationClassPostProcessor#processConfigBeanDefinitions

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
	List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
	String[] candidateNames = registry.getBeanDefinitionNames();

	for (String beanName : candidateNames) {
		BeanDefinition beanDef = registry.getBeanDefinition(beanName);
		if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
				ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
			if (logger.isDebugEnabled()) {
				logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
			}
		}
		else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {	// #1
			configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
		}
	}
	if (configCandidates.isEmpty()) {
		return;
	}

	...
	
	ConfigurationClassParser parser = new ConfigurationClassParser(
			this.metadataReaderFactory, this.problemReporter, this.environment,
			this.resourceLoader, this.componentScanBeanNameGenerator, registry);	//#2

	Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
	Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
	do {
		parser.parse(candidates);	//#3
		parser.validate();

		Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());	// #4
		configClasses.removeAll(alreadyParsed);

		if (this.reader == null) {
			this.reader = new ConfigurationClassBeanDefinitionReader(
					registry, this.sourceExtractor, this.resourceLoader, this.environment,
					this.importBeanNameGenerator, parser.getImportRegistry());
		}
		this.reader.loadBeanDefinitions(configClasses);	// #5
		alreadyParsed.addAll(configClasses);

		candidates.clear();
		if (registry.getBeanDefinitionCount() > candidateNames.length) {
			String[] newCandidateNames = registry.getBeanDefinitionNames();	// #6
			Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
			Set<String> alreadyParsedClasses = new HashSet<>();
			for (ConfigurationClass configurationClass : alreadyParsed) {
				alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
			}
			for (String candidateName : newCandidateNames) {
				if (!oldCandidateNames.contains(candidateName)) {
					BeanDefinition bd = registry.getBeanDefinition(candidateName);	
					if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
							!alreadyParsedClasses.contains(bd.getBeanClassName())) {
						candidates.add(new BeanDefinitionHolder(bd, candidateName));	// #7
					}
				}
			}
			candidateNames = newCandidateNames;
		}
	}
	while (!candidates.isEmpty());	// #8

	...
}

#1 检查bean是否为ConfigurationClass,这里主要是检查class注解信息(spring中将@Configuration标注的类归类为ConfigurationClass)
#2 构建ConfigurationClassParser
#3 解析ConfigurationClass
#4 获取结果,注意ConfigurationClassParser#getConfigurationClasses方法获取ConfigurationClassParser的处理结果
#5 获取ConfigurationClass引入的Class,将其转化为BeanDefinition,并注册到Spring上下文
最后构造bean,是在AbstractApplicationContext#refresh方法中,调用finishBeanFactoryInitialization,构建热加载的单例bean时完成。
#6 获取新的BeanDefinition列表
#7 如果前面的ConfigurationClass有引入了新的ConfigurationClass,添加到待处理集合
#8 循环处理,直到待处理集合为空

ConfigurationClassParser#parse -> ConfigurationClassParser#processConfigurationClass

protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
	if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {	// #1
		return;
	}

	...

	SourceClass sourceClass = asSourceClass(configClass);	// #2
	do {
		sourceClass = doProcessConfigurationClass(configClass, sourceClass);	// #3
	}
	while (sourceClass != null);	// #4

	this.configurationClasses.put(configClass, configClass);	// #5
}

#1 检查ConfigurationClass是否存在@Conditional注解,如果存在,取注解中Condition条件判断类进行判断 #2 将ConfigurationClass转化为SourceClass
SourceClass对Class元数据进行封装,可以兼容处理JVM加载的Class和ASM读取的元数据,获取元数据中注解,方法等信息
#3 doProcessConfigurationClass方法很关键,处理@Component,@PropertySources,@ComponentScans,@Import,@ImportResource,带@Bean的方法,接口及父类。
#4 如果ConfigurationClass存在父类,doProcessConfigurationClass返回父类,这里递归处理父类数据
#5 将该ConfigurationClass加入configurationClasses,以便ConfigurationClassPostProcessor#processConfigBeanDefinitions方法#5步骤使用。
该方法是处理ConfigurationClass的入口,doProcessConfigurationClass中引入了新的ConfigurationClass,也会调用该方法处理。

ConfigurationClassParser#doProcessConfigurationClass

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
		throws IOException {
	if (configClass.getMetadata().isAnnotated(Component.class.getName())) {	
		processMemberClasses(configClass, sourceClass);	// #1
	}

	...

	processImports(configClass, sourceClass, getImports(sourceClass), true);	// #2

	AnnotationAttributes importResource =
			AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);	// #3
	if (importResource != null) {
		String[] resources = importResource.getStringArray("locations");
		Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
		for (String resource : resources) {
			String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
			configClass.addImportedResource(resolvedResource, readerClass);
		}
	}

	Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);	// #4
	for (MethodMetadata methodMetadata : beanMethods) {
		configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
	}

	processInterfaces(configClass, sourceClass);	// #5

	if (sourceClass.getMetadata().hasSuperClass()) {	// #6
		String superclass = sourceClass.getMetadata().getSuperClassName();
		if (superclass != null && !superclass.startsWith("java") &&
				!this.knownSuperclasses.containsKey(superclass)) {
			this.knownSuperclasses.put(superclass, configClass);
			return sourceClass.getSuperClass();
		}
	}

	return null;
}

这里只关注该方法AutoConfigure功能相关的代码
#1 如果Class存在@Component注解,会查询Class的内部类,如果内部类也是ConfigurationClass,会调用processConfigurationClass方法处理内部类(注意,@Configuration注解上标识了@Component注解)
#2 处理@Import注解
#3 处理@ImportResource,添加引入资源信息到ConfigurationClass#importedResources,ConfigurationClassPostProcessor#processConfigBeanDefinitions方法#5步骤会处理
#4 选择Class中存在@Bean标注的方法,加入到ConfigurationClass#beanMethods中
#5 选择接口中存在@Bean标注的方法,同样加入到ConfigurationClass#beanMethods中
#6 如果存在父类,返回父类到ConfigurationClassParser#processConfigurationClass中,递归处理父类。

ConfigurationClassParser#processImports

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)) {	// #1
					Class<?> candidateClass = candidate.loadClass();
					ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
					ParserStrategyUtils.invokeAwareMethods(
							selector, this.environment, this.resourceLoader, this.registry);	
					if (selector instanceof DeferredImportSelector) {	// #2
						this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
					}
					else {
						String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());	// #3
						Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);	// #4
						processImports(configClass, currentSourceClass, importSourceClasses, false);	//#5
					}
				}
				else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {	// #6
					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());	//#7
				}
				else {	
					this.importStack.registerImport(
							currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
					processConfigurationClass(candidate.asConfigClass(configClass));	//#8
				}
			}
		}
		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();
		}
	}
}

#1 @Import引入的类是ImportSelector实现类
#2 DeferredImportSelector接口需要延迟处理,加入到deferredImportSelectorHandler中
ConfigurationClassParser#parse方法最后会调用deferredImportSelectorHandler#process方法处理DeferredImportSelector接口
#3 调用ImportSelector#selectImports方法
#4 使用ImportSelector#selectImports返回Class Name数组,加载对应的SourceClass
#5 使用processImports方法继续处理这些SourceClass
#6 @Import引入的类是ImportBeanDefinitionRegistrar实现类
#7 将该类加入到ConfigurationClass#importBeanDefinitionRegistrars中,ConfigurationClassPostProcessor#processConfigBeanDefinitions方法#5步骤会处理
#8 @Import引入的类是其他类,转发为ConfigurationClass,使用processConfigurationClass方法处理

这里对应了@Import注解的三种用法,引入ImportSelector,ImportBeanDefinitionRegistrar或者具体的ConfigurationClass。

@Import最后都要processConfigurationClass处理它引入的ConfigurationClass

回到ConfigurationClassPostProcessor#processConfigBeanDefinitions方法#5步骤,
ConfigurationClassBeanDefinitionReader#loadBeanDefinitions -> loadBeanDefinitionsForConfigurationClass

private void loadBeanDefinitionsForConfigurationClass(
		ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {

	...

	if (configClass.isImported()) {
		registerBeanDefinitionForImportedConfigurationClass(configClass);	// #1
	}
	for (BeanMethod beanMethod : configClass.getBeanMethods()) {
		loadBeanDefinitionsForBeanMethod(beanMethod);	// #2
	}

	loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());	// #3
	loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());	// #4
}

#1 注册ConfigurationClass自身的BeanDefinition
#2 注册@Bean注解标识方法引入的bean
#3 从@ImportResource引入的资源中读取BeanDefinition
#4 处理@Import引入的ImportBeanDefinitionRegistrar

@EnableAutoConfiguration注解引入的AutoConfigurationImportSelector,实现的是DeferredImportSelector接口 AutoConfigurationImportSelector#selectImports -> getAutoConfigurationEntry

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

#1 从spring.factories文件中获取@EnableAutoConfiguration对应的ConfigurationClass
#3 排除spring.autoconfigure.exclude配置的ConfigurationClass
#4 使用spring.factories中配置的AutoConfigurationImportFilter的实现类(OnBeanCondition,OnClassCondition,OnWebApplicationCondition)过滤部分ConfigurationClass,这里处理@ConditionalOnBean,@ConditionalOnClass,@ConditionalOnMissingClass等注解。
OnClassCondition可以判断当前Java应用中存在或者不存在某一个class,SpringBoot AutoConfigure功能可以实现当我们引入某个框架jar后,自动配置完成该框架的配置,正是通过该条件判断类实现。

来看一个例子,RedisAutoConfiguration

@Configuration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {

	@Bean
	@ConditionalOnMissingBean(name = "redisTemplate")
	public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
			throws UnknownHostException {
		RedisTemplate<Object, Object> template = new RedisTemplate<>();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}
	...
}

RedisAutoConfiguration是一个ConfigurationClass,他使用@Bean标识方法引入其他bean
(RedisAutoConfiguration在spring-boot-autoconfigure这个jar下的spring.factories文件中已经被配置为@EnableAutoConfiguration的ConfigurationClass)

@ConditionalOnClass表明classpath只有存在RedisOperations这个类,RedisAutoConfiguration的配置才生效 (引入spring-data-redis的jar后有这个类了,RedisAutoConfiguration也就生效了)
@Import引入的 LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class用于与redis建立连接,并生成RedisConnectionFactory。
同样,引入 Lettuce相关jar后,LettuceConnectionConfiguration生效,引入Jedis相关jar后,JedisConnectionConfiguration生效。

如果您觉得本文不错,欢迎关注我的微信公众号,您的关注是我坚持的动力!