Spring Boot 自动配置实现原理(源码分析)

前言:

我们都在为 Spring Boot 强大的自动配置功能带来的效率提升而感叹,那你是否了解自动配置的原理?本篇我们就来分析一下 Spring Boot 的自动配置原理。

Spring Boot 系列文章传送门

Spring Boot 启动流程源码分析(2)

Spring Boot 启动流程源码分析(2)

什么是 Spring Boot 自动配置?

自动配置 Auto-Configuration,是指基于你引入的依赖 Jar 包,对 Spring Boot 应用进行自动配置,自动配置的目标是减少开发者在开始一个新项目或者给现有项目添加新特性时的工作量,避免需要使用大量的配置,Spring Boot 通过在应用的启动阶段应用一些"约定优于配置"的原则来实现这一点,自动配置为 SpringBoot 框架的【开箱即用】提供了重要的基础支撑。

自动配置和自动装配的区别?

  • 自动配置: Auto-Configuration,针对的是 SpringBoot 中的配置类。
  • 自动装配: Autowire,针对是 Spring 中的依赖注入。

@SpringBootApplication 注解

我们都知道 Spring Boot 项目只需要在启动类上加上 @SpringBootApplication 注解,即可完成自动配置,那你了解 @SpringBootApplication 注解吗?

@SpringBootApplication 注解是一个组合注解,如下:

  • @SpringBootConfiguration:继承自 Configuration,支持 Java Config 方式进行配置。
  • @EnableAutoConfiguration:最最核心的注解,主要用于开启自动配置(本篇研究的重点)。
  • @ComponentScan:自动扫描组件,默认扫描该类所在包及其子包下所有带有指定注解的类,将它们自动装配到bean容器中,会被自动装配的注解包括@Controller、@Service、@Component、@Repository等。也可以指定扫描路径。

@SpringBootApplication 注解详解传送门:

@SpringBootApplication 注解到底做了什么?你真的知道吗?

@EnableAutoConfiguration 注解源码解析

@EnableAutoConfiguration 注解也是一个组合注解,引用了 @AutoConfigurationPackage 注解,以及导入了一个 AutoConfigurationImportSelector 类,我们重点分析 AutoConfigurationImportSelector类。

package org.springframework.boot.autoconfigure;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Import;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};

    String[] excludeName() default {};
}

AutoConfigurationImportSelector#selectImports 方法源码分析

AutoConfigurationImportSelector 类实现了 DeferredImportSelector 接口,DeferredImportSelector 又实现了 ImportSelector 接口,根据@Import 注解的原理,实际会调用 AutoConfigurationImportSelector#selectImports 方法实现类的加载。

//org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#selectImports
public String[] selectImports(AnnotationMetadata annotationMetadata) {
	//判断是否注解元数据 自动配置是否打开
	if (!this.isEnabled(annotationMetadata)) {
		return NO_IMPORTS;
	} else {
		//从配置文件加载 AutoConfigurationMetadata
		AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
		//获取所有配置类 重点关注  this.getAutoConfigurationEntry 这行代码
		AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	}
}

AutoConfigurationImportSelector#getAutoConfigurationEntry 方法源码分析

AutoConfigurationImportSelector#getAutoConfigurationEntry 方法主要是加载 META-INF/spring.factories 文件中声明的配置类,并将一些配置类过滤掉。

//org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getAutoConfigurationEntry
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
	//判断是否注解元数据 自动配置是否打开
	if (!this.isEnabled(annotationMetadata)) {
		return EMPTY_ENTRY;
	} else {
		//获取 EnableAutoConfiguration`注解中的属性
		AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
		//加载 META-INF/spring.factories 文件中声明的配置类 重点关注
		List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
		//移出重复的配置类
		configurations = this.removeDuplicates(configurations);
		//获取注解的排除项
		Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
		this.checkExcludedClasses(configurations, exclusions);
		//移出需要排除的
		configurations.removeAll(exclusions);
		//过滤掉不需要的配置类 spring-autoconfigure-metadata.properties
		configurations = this.filter(configurations, autoConfigurationMetadata);
		//发布自动配置导入事件
		this.fireAutoConfigurationImportEvents(configurations, exclusions);
		return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
	}
}

AutoConfigurationImportSelector#getCandidateConfigurations 方法源码分析

AutoConfigurationImportSelector#getCandidateConfigurations 方法主要是调用了 SpringFactoriesLoader#loadFactoryNames 方法,读取 META-INF/spring.factories 文件中配置的类名返回。

//org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getCandidateConfigurations
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
	//调用 Spring 提供的方法读取 META-INF/spring.factories 文件中配置的类名
	List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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;
}


//org.springframework.core.io.support.SpringFactoriesLoader#loadFactoryNames
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
	//接口名称
	String factoryTypeName = factoryType.getName();
	//获取接口的实现类
	return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}

//org.springframework.core.io.support.SpringFactoriesLoader#loadFactoryNames
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
	//从缓存中获取
	MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
	if (result != null) {
		//缓存不为空 字节返回
		return result;
	} else {
		try {
			//从 META-INF/spring.factories 加载资源文件 META-INF/spring.factories 熟悉的名词出现了
			Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
			//创建空 map
			LinkedMultiValueMap result = new LinkedMultiValueMap();
			//遍历加载到的资源文件
			while(urls.hasMoreElements()) {
				//获取 url
				URL url = (URL)urls.nextElement();
				//根据 url 创建 Resource
				UrlResource resource = new UrlResource(url);
				//加载资源文件 资源文件是 properties 格式的 key 是接口名 value 是实现类名称 多个实现类 用英文逗号隔开
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				//迭代遍历
				Iterator var6 = properties.entrySet().iterator();

				while(var6.hasNext()) {
					Entry<?, ?> entry = (Entry)var6.next();
					//获取 key 
					String factoryTypeName = ((String)entry.getKey()).trim();
					//获取 value 数组
					String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
					int var10 = var9.length;
					//遍历加入到结果集
					for(int var11 = 0; var11 < var10; ++var11) {
						String factoryImplementationName = var9[var11];
						result.add(factoryTypeName, factoryImplementationName.trim());
					}
				}
			}
			//加入缓存
			cache.put(classLoader, result);
			//返回
			return result;
		} catch (IOException var13) {
			throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
		}
	}
}

接下来我们总结一下 AutoConfigurationImportSelector#selectImports 实现自动配置的过程:

  1. 判断自动配置是否开启,默认是开启的,可以自己配置关闭,但没必要。
  2. 获取 @EnableAutoConfiguration 注解的属性。
  3. 加载项目下所有 META-INF/spring.factories 文件中声明的配置类。
  4. 对加载的配置类进行各种去重,包括去掉指定排除的配置类,过滤掉一些不需要的配置类,最终返回需要加载的配置类。

AutoConfigurationImportSelector#selectImports 方法没有走为什么?

上面我们在分析自动配置实现原理的时分析到,@EnableAutoConfiguration 注解通过 @Import 注解导入了一个 ImportSelector 接口的实现类 AutoConfigurationImportSelector,按照 @Import 注解的功能来说,按道理来说项目启动时候会执行 AutoConfigurationImportSelector#selectImports 方法,但是我在启动调试的时候,发现并没有走 AutoConfigurationImportSelector#selectImports 方法,但是确实又走到了 AutoConfigurationImportSelector#getAutoConfigurationEntry 方法,那这是为什么呢?其根本原因在于 AutoConfigurationImportSelector 实现的这个接口 DeferredImportSelector 有点特殊,上面也说了 AutoConfigurationImportSelector 实现了 DeferredImportSelector 接口。

DeferredImportSelector 类源码分析

上面我们说项目启动的时候没有调用 AutoConfigurationImportSelector#selectImports,原因是 AutoConfigurationImportSelector 实现的接口 DeferredImportSelector 有些特殊,我们简单的看下 DeferredImportSelector 的源码,如下:

package org.springframework.context.annotation;

import org.springframework.core.type.AnnotationMetadata;
import org.springframework.lang.Nullable;

public interface DeferredImportSelector extends ImportSelector {
	
	//注意这个方法 getImportGroup
    @Nullable
    default Class<? extends DeferredImportSelector.Group> getImportGroup() {
        return null;
    }
	
	//内部 Group 接口
    public interface Group {
		
		//process 方法
        void process(AnnotationMetadata var1, DeferredImportSelector var2);
		
		//selectImports  方法  名称似乎有点熟悉
        Iterable<DeferredImportSelector.Group.Entry> selectImports();

        public static class Entry {
            private final AnnotationMetadata metadata;
            private final String importClassName;

            public Entry(AnnotationMetadata metadata, String importClassName) {
                this.metadata = metadata;
                this.importClassName = importClassName;
            }

            public AnnotationMetadata getMetadata() {
                return this.metadata;
            }

            public String getImportClassName() {
                return this.importClassName;
            }

            public boolean equals(@Nullable Object other) {
                if (this == other) {
                    return true;
                } else if (other != null && this.getClass() == other.getClass()) {
                    DeferredImportSelector.Group.Entry entry = (DeferredImportSelector.Group.Entry)other;
                    return this.metadata.equals(entry.metadata) && this.importClassName.equals(entry.importClassName);
                } else {
                    return false;
                }
            }

            public int hashCode() {
                return this.metadata.hashCode() * 31 + this.importClassName.hashCode();
            }

            public String toString() {
                return this.importClassName;
            }
        }
    }
}

从 DeferredImportSelector 的源码中我们发现了它有一个内部接口 Group,而 Group 又有一个方法叫做 selectImports,这个方法名有点熟悉,AutoConfigurationImportSelector 类中有一个同样的方法,因此我们是否可以大胆猜测项目启动的时候走到了 DeferredImportSelector.Group#selectImports 方法【虽然后面证实了这个猜测不够准确,但是我们还是可以去大胆推测的】。

进一步推测

DeferredImportSelector 源码中有一个 getImportGroup 方法,我们去 AutoConfigurationImportSelector 源码中找到了方法的实现,如下:

//org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getImportGroup
public Class<? extends Group> getImportGroup() {
	return AutoConfigurationImportSelector.AutoConfigurationGroup.class;
}

根据源码发现 getImportGroup 方法返回的是 AutoConfigurationImportSelector 的内部类 AutoConfigurationGroup 的 Class,我们继续跟踪 AutoConfigurationGroup 类。

AutoConfigurationImportSelector.AutoConfigurationGroup 类源码分析

这里我们只简单贴出两个重点方法,process 方法和 selectImports 方法,我们在 process 方法中看到了 AutoConfigurationImportSelector#getAutoConfigurationEntry 方法,这个方法在项目启动时候是调用了的,因此我们可以进一步猜测,项目启动的时候是调用了 AutoConfigurationImportSelector.AutoConfigurationGroup#process 方法。

//org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup#process
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
	Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector, () -> {
		return String.format("Only %s implementations are supported, got %s", AutoConfigurationImportSelector.class.getSimpleName(), deferredImportSelector.getClass().getName());
	});
	//getAutoConfigurationEntry 熟悉的名称出现了
	AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector)deferredImportSelector).getAutoConfigurationEntry(this.getAutoConfigurationMetadata(), annotationMetadata);
	this.autoConfigurationEntries.add(autoConfigurationEntry);
	Iterator var4 = autoConfigurationEntry.getConfigurations().iterator();

	while(var4.hasNext()) {
		String importClassName = (String)var4.next();
		this.entries.putIfAbsent(importClassName, annotationMetadata);
	}

}


//org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup#selectImports
public Iterable<Entry> selectImports() {
	if (this.autoConfigurationEntries.isEmpty()) {
		return Collections.emptyList();
	} else {
		Set<String> allExclusions = (Set)this.autoConfigurationEntries.stream().map(AutoConfigurationImportSelector.AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet());
		Set<String> processedConfigurations = (Set)this.autoConfigurationEntries.stream().map(AutoConfigurationImportSelector.AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream).collect(Collectors.toCollection(LinkedHashSet::new));
		processedConfigurations.removeAll(allExclusions);
		return (Iterable)this.sortAutoConfigurations(processedConfigurations, this.getAutoConfigurationMetadata()).stream().map((importClassName) -> {
			return new Entry((AnnotationMetadata)this.entries.get(importClassName), importClassName);
		}).collect(Collectors.toList());
	}
}

猜想验证

简单的启动一个 Spring Boot 项目,进行 debugger 调试。

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

AutoConfigurationImportSelector.AutoConfigurationGroup#process 方法

在这里插入图片描述

AutoConfigurationImportSelector.AutoConfigurationGroup#selectImports 方法

在这里插入图片描述
debugger 结果验证了我们的猜想,项目启动最终走到了AutoConfigurationImportSelector 内部类 AutoConfigurationGroup 的 process 方法和 selectImports 方法,完成了自动配置。

原理分析

上面我们通过 debugger 的方式验证了我们的猜想,项目启动最终走到了AutoConfigurationImportSelector 内部类 AutoConfigurationGroup 的 process 方法和 selectImports 方法,完成了自动配置,但是 Spring Boot 是从哪里对 DeferredImportSelector 接口做特殊处理,又为啥要这么设计呢?下面我们从源码层面去剖析。

我们通过 debugger 调试的方式,找到方法的调用栈,把断点打在 AutoConfigurationImportSelector#getAutoConfigurationEntry 方法上,得到如下调用栈截图。

在这里插入图片描述

方法的调用栈上可以看到非常多我们属性的方法,例如:SpringApplication#refresh 、AbstractApplicationContext#refresh、AbstractApplicationContext#invokeBeanFactoryPostProcess、ConfigurationClassParser#parse(重点关注,分析的入口)、AutoConfigurationImportSelector$AutoConfigurationGroup#process 等方法。

简单梳理一下调用栈:

  1. 启动 main 方法,掉用 SpringApplication#run 方法。
  2. SpringApplication#run 方法最终会调用 AbstractApplicationContext#refresh 进行 IOC 容器的初始化。
  3. AbstractApplicationContext#refresh 方法 会调用 AbstractApplicationContext#invokeBeanFactoryPostProcess 方法,这里会调用所有的BeanFactory 后处理器。
  4. BeanFactory 中有一个叫做 ConfigurationClassPostProcessor 的处理器,负责处理@Configuration、@Import 等注解,是我们本篇关注的重点。
  5. 接着就会调用 ConfigurationClassParser#parse 方法,本篇重点分析的方法。
  6. 再接下来就会调用到 AutoConfigurationImportSelector$AutoConfigurationGroup#process 方法,进一步调用 AutoConfigurationImportSelector#getAutoConfigurationEntry 方法,完成自动配置。

ConfigurationClassParser#parse 源码分析

ConfigurationClassParser#parse 方法遍历所有的 BeanDefinitionHolder,根据 BeanDefinitionHolder 的类型不同,进行不同的解析操作,这里我们重点关注注解类型的解析,解析完成之后会执行 DeferredImportSelectorHandler#process 方法,这个方法我们最后再看。

//org.springframework.context.annotation.ConfigurationClassParser#parse(java.util.Set<org.springframework.beans.factory.config.BeanDefinitionHolder>)
public void parse(Set<BeanDefinitionHolder> configCandidates) {
	//遍历所有配置项的 BeanDefinitionHolder
	Iterator var2 = configCandidates.iterator();

	while(var2.hasNext()) {
		//从 BeanDefinitionHolder 中获取  BeanDefinition
		BeanDefinitionHolder holder = (BeanDefinitionHolder)var2.next();
		BeanDefinition bd = holder.getBeanDefinition();

		try {
			//根据不同类型的 BeanDefinition 执行不同的解析逻辑
			if (bd instanceof AnnotatedBeanDefinition) {
				//注解的 beandefinition @Import注解就在这里解析
				this.parse(((AnnotatedBeanDefinition)bd).getMetadata(), holder.getBeanName());
			} else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition)bd).hasBeanClass()) {
				//抽象的 beandefination 有 class 的
				this.parse(((AbstractBeanDefinition)bd).getBeanClass(), holder.getBeanName());
			} else {
				//其他
				this.parse(bd.getBeanClassName(), holder.getBeanName());
			}
		} catch (BeanDefinitionStoreException var6) {
			throw var6;
		} catch (Throwable var7) {
			throw new BeanDefinitionStoreException("Failed to parse configuration class [" + bd.getBeanClassName() + "]", var7);
		}
	}
	
	//执行找到的DeferredImportSelector 的 process 方法
	this.deferredImportSelectorHandler.process();
}

ConfigurationClassParser#parse 方法源码解析

ConfigurationClassParser#parse 方法并没有什么实际操作,调用了 ConfigurationClassParser#processConfigurationClass,该方法会判断是否解析过,并且会多次解析情况进行处理,最终调用 ConfigurationClassParser#doProcessConfigurationClass 方法完成解析。

//org.springframework.context.annotation.ConfigurationClassParser#parse(org.springframework.core.type.AnnotationMetadata, java.lang.String)
protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
	this.processConfigurationClass(new ConfigurationClass(metadata, beanName), DEFAULT_EXCLUSION_FILTER);
}


//org.springframework.context.annotation.ConfigurationClassParser#processConfigurationClass
protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
	//判断是否解析过
	if (!this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
		//通过配置类名称获取ConfigurationClass
		ConfigurationClass existingClass = (ConfigurationClass)this.configurationClasses.get(configClass);
		//判断是否为null
		if (existingClass != null) {
			//不为null  那就是再次 import
			if (configClass.isImported()) {
				if (existingClass.isImported()) {
					//已经存在了 就合并
					existingClass.mergeImportedBy(configClass);
				}

				return;
			}

			this.configurationClasses.remove(configClass);
			this.knownSuperclasses.values().removeIf(configClass::equals);
		}
		//处理配置类
		ConfigurationClassParser.SourceClass sourceClass = this.asSourceClass(configClass, filter);

		do {
			//解析操作
			sourceClass = this.doProcessConfigurationClass(configClass, sourceClass, filter);
		} while(sourceClass != null);
		//存储解析的配置类  回到 parse  方法可以获取到
		this.configurationClasses.put(configClass, configClass);
	}
}

ConfigurationClassParser#doProcessConfigurationClass 方法源码解析

ConfigurationClassParser#doProcessConfigurationClass 方法中有对各种注解的解析,除了我们重点关注的 @Import 注解之外,还有 @ImportResource、@Bean 注解,该方法中也考虑到递归解析的情况。

//org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass
@Nullable
protected final ConfigurationClassParser.SourceClass doProcessConfigurationClass(ConfigurationClass configClass, ConfigurationClassParser.SourceClass sourceClass, Predicate<String> filter) throws IOException {
	//@Component 注解
	if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
		// 递归处理内部类 因为内部类也是一个配置类 配置类上有@configuration注解 继承@Component 调用 processMemberClasses 方法 递归解析配置类中的内部类
		this.processMemberClasses(configClass, sourceClass, filter);
	}

	//配置类上加了 @PropertySource 注解 解析加载 properties 文件 并将属性添加到 spring 上下文中
	Iterator var4 = AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), PropertySources.class, PropertySource.class).iterator();

	AnnotationAttributes importResource;
	while(var4.hasNext()) {
		importResource = (AnnotationAttributes)var4.next();
		if (this.environment instanceof ConfigurableEnvironment) {
			this.processPropertySource(importResource);
		} else {
			this.logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() + "]. Reason: Environment must implement ConfigurableEnvironment");
		}
	}

	//处理 @ComponentScan @ComponentScans 注解 并将扫描包下的所有 bean 转换成填充后的 ConfigurationClass
	Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
	if (!componentScans.isEmpty() && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
		//componentScans 不为空  且没有解析过 迭代遍历解析
		Iterator var14 = componentScans.iterator();

		while(var14.hasNext()) {
			AnnotationAttributes componentScan = (AnnotationAttributes)var14.next();
			//解析 @ComponentScan @ComponentScans 配置的包下的类  得到 BeanDefinitionHolder
			Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
			//迭代遍历
			Iterator var8 = scannedBeanDefinitions.iterator();

			while(var8.hasNext()) {
				BeanDefinitionHolder holder = (BeanDefinitionHolder)var8.next();
				BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
				if (bdCand == null) {
					bdCand = holder.getBeanDefinition();
				}
				//判断是否是一个配置类
				if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
					//递归解析
					this.parse(bdCand.getBeanClassName(), holder.getBeanName());
				}
			}
		}
	}
	//处理 @Import 注解 重点关注
	this.processImports(configClass, sourceClass, this.getImports(sourceClass), filter, true);
	//处理 @ImportResource 注解 导入 Spring  配置文件
	importResource = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
	if (importResource != null) {
		String[] resources = importResource.getStringArray("locations");
		Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
		String[] var20 = resources;
		int var22 = resources.length;

		for(int var23 = 0; var23 < var22; ++var23) {
			String resource = var20[var23];
			String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
			configClass.addImportedResource(resolvedResource, readerClass);
		}
	}
	//处理加了 @Bean  注解的方法
	Set<MethodMetadata> beanMethods = this.retrieveBeanMethodMetadata(sourceClass);
	Iterator var18 = beanMethods.iterator();

	while(var18.hasNext()) {
		MethodMetadata methodMetadata = (MethodMetadata)var18.next();
		//转换为 BeanMethod 对象
		configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
	}
	//处理接口的默认实现
	this.processInterfaces(configClass, sourceClass);
	//解析父类 如果被解析的配置类继承了某个类 那么配置类的父类也会被进行解析
	if (sourceClass.getMetadata().hasSuperClass()) {
		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;
}

ConfigurationClassParser#processImports 方法源码解析

ConfigurationClassParser#processImports 方法在处理时分成了 3 个类型,分别是实现了 ImportSelector 接口的,实现 ImportBeanDefinitionRegistrar 接口的,其他类型的,我们重点关注的是实现 ImportSelector 接口的。

//org.springframework.context.annotation.ConfigurationClassParser#processImports
private void processImports(ConfigurationClass configClass, ConfigurationClassParser.SourceClass currentSourceClass, Collection<ConfigurationClassParser.SourceClass> importCandidates, Predicate<String> exclusionFilter, boolean checkForCircularImports) {
	//为空判断
	if (!importCandidates.isEmpty()) {
		if (checkForCircularImports && this.isChainedImportOnStack(configClass)) {
			//是否循环导入  是否堆栈上的链式导入  是的话  error
			this.problemReporter.error(new ConfigurationClassParser.CircularImportProblem(configClass, this.importStack));
		} else {
			
			this.importStack.push(configClass);

			try {
				//迭代遍历需要导入的类
				Iterator var6 = importCandidates.iterator();

				while(var6.hasNext()) {
					//获取要导入的类
					ConfigurationClassParser.SourceClass candidate = (ConfigurationClassParser.SourceClass)var6.next();
					Class candidateClass;
					//判断要导入的类 是否是 ImportSelector 的子类
					if (candidate.isAssignable(ImportSelector.class)) {
						//要导入的类是一个导入选择器  委托来确定是否进行导入
						candidateClass = candidate.loadClass();
						//反射生成一个 ImportSelect 对象
						ImportSelector selector = (ImportSelector)ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class, this.environment, this.resourceLoader, this.registry);
						//获取选择器的排除过滤器
						Predicate<String> selectorFilter = selector.getExclusionFilter();
						if (selectorFilter != null) {
							exclusionFilter = exclusionFilter.or(selectorFilter);
						}
						//判断引用选择器是否是 DeferredImportSelector 接口的实例
						if (selector instanceof DeferredImportSelector) {
							//是 将选择器添加到 deferredImportSelectorHandler 实例中 等到所有的配置类加载完成后统一处理自动化配置类
							this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector)selector);
						} else {
							//否  获取引入的类 然后使用递归方式将这些类中同样添加了 @Import 注解引用的类
							String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
							Collection<ConfigurationClassParser.SourceClass> importSourceClasses = this.asSourceClasses(importClassNames, exclusionFilter);
							//递归处理 被 Import 进来的类可能也有 @Import 注解
							this.processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
						}
					} else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
						实现ImportBeanDefinitionRegistrar接口的
						candidateClass = candidate.loadClass();
						ImportBeanDefinitionRegistrar registrar = (ImportBeanDefinitionRegistrar)ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class, this.environment, this.resourceLoader, this.registry);
						configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
					} else {
						//其他情况
						this.importStack.registerImport(currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
						this.processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
					}
				}
			} catch (BeanDefinitionStoreException var17) {
				throw var17;
			} catch (Throwable var18) {
				throw new BeanDefinitionStoreException("Failed to process import candidates for configuration class [" + configClass.getMetadata().getClassName() + "]", var18);
			} finally {
				this.importStack.pop();
			}
		}

	}
}

DeferredImportSelectorHandler#handle 方法源码解析

DeferredImportSelectorHandler#handle 方法逻辑很简单,只是注册和存储,但不会真正执行。

//org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorHandler#handle
public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) {
	//DeferredImportSelectorHolder 获取 holder
	ConfigurationClassParser.DeferredImportSelectorHolder holder = new ConfigurationClassParser.DeferredImportSelectorHolder(configClass, importSelector);
	//为空判断
	if (this.deferredImportSelectors == null) {
		//创建一个 DeferredImportSelectorGroupingHandler
		ConfigurationClassParser.DeferredImportSelectorGroupingHandler handler = ConfigurationClassParser.this.new DeferredImportSelectorGroupingHandler();
		//注册 DeferredImportSelectorHolder
		handler.register(holder);
		//重点方法  会调用到  AutoConfigurationGroup#process
		handler.processGroupImports();
	} else {
		//加入到 deferredImportSelectors
		this.deferredImportSelectors.add(holder);
	}

}

DeferredImportSelectorGroupingHandler#register 方法源码分析

DeferredImportSelectorGroupingHandler#register 方法主要完成 DeferredImportSelectorHolder 的注册。

//org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorGroupingHandler#register
public void register(ConfigurationClassParser.DeferredImportSelectorHolder deferredImport) {
	//获取 getImportGroup 方法返回值 getImportGroup 方法上面提到过的
	Class<? extends Group> group = deferredImport.getImportSelector().getImportGroup();
	//如果为空就说明没有重写 使用原来的 deferredImport 对象
	ConfigurationClassParser.DeferredImportSelectorGrouping grouping = (ConfigurationClassParser.DeferredImportSelectorGrouping)this.groupings.computeIfAbsent(group != null ? group : deferredImport, (key) -> {
		return new ConfigurationClassParser.DeferredImportSelectorGrouping(this.createGroup(group));
	});
	//加入到 grouping 中
	grouping.add(deferredImport);
	//加入到 configurationClasses
	this.configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(), deferredImport.getConfigurationClass());
}

DeferredImportSelectorGroupingHandler#processGroupImports 方法源码解析

DeferredImportSelectorGroupingHandler#processGroupImports 方法即将完成最终的调用,重点在 grouping.getImports() 这行代码,我们接着分析。

//org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorGroupingHandler#processGroupImports
public void processGroupImports() {
	Iterator var1 = this.groupings.values().iterator();

	while(var1.hasNext()) {
		ConfigurationClassParser.DeferredImportSelectorGrouping grouping = (ConfigurationClassParser.DeferredImportSelectorGrouping)var1.next();
		Predicate<String> exclusionFilter = grouping.getCandidateFilter();
		// getImports 方法内部会调用默认的或者我们覆盖过的 process 方法和 selectImports 方法
		grouping.getImports().forEach((entry) -> {
			ConfigurationClass configurationClass = (ConfigurationClass)this.configurationClasses.get(entry.getMetadata());

			try {
				//配置类中可能会包含 @Import 注解引入的类 通过此方法将引入的类注入
				ConfigurationClassParser.this.processImports(configurationClass, ConfigurationClassParser.this.asSourceClass(configurationClass, exclusionFilter), Collections.singleton(ConfigurationClassParser.this.asSourceClass(entry.getImportClassName(), exclusionFilter)), exclusionFilter, false);
			} catch (BeanDefinitionStoreException var5) {
				throw var5;
			} catch (Throwable var6) {
				throw new BeanDefinitionStoreException("Failed to process import candidates for configuration class [" + configurationClass.getMetadata().getClassName() + "]", var6);
			}
		});
	}

}

DeferredImportSelectorGrouping#getImports 方法源码分析

DeferredImportSelectorGrouping#getImports 方法中会调用到文章开头我们提到的 AutoConfigurationImportSelector.AutoConfigurationGroup#process 和 AutoConfigurationImportSelector.AutoConfigurationGroup#selectImports 方法。

//org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorGrouping#getImports
public Iterable<Entry> getImports() {
	//迭代遍历  deferredImports 其实就是 DeferredImportSelectorHolder
	Iterator var1 = this.deferredImports.iterator();

	while(var1.hasNext()) {
		ConfigurationClassParser.DeferredImportSelectorHolder deferredImport = (ConfigurationClassParser.DeferredImportSelectorHolder)var1.next();
		//AutoConfigurationImportSelector.AutoConfigurationGroup#process  方法
		this.group.process(deferredImport.getConfigurationClass().getMetadata(), deferredImport.getImportSelector());
	}
	//执行 AutoConfigurationImportSelector.AutoConfigurationGroup#selectImports  方法
	return this.group.selectImports();
}

至此,我们就知道为什么默认的 AutoConfigurationImportSelector#selectImports 方法没有执行,而是执行了 AutoConfigurationImportSelector.AutoConfigurationGroup#process 。

总结:如果没有通过大胆猜测、debugger 调试、源码跟踪,我们大概率会任务 Spring Boot 完成自动配置会走 AutoConfigurationImportSelector#selectImports 方法,这个结论明显是错误的,希望本篇的分享可以帮助到有需要的小伙伴们。
如有不正确的地方请各位指出纠正。

  • 35
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值