springboot之自动配置原理分析

写在前面

在Java中各种框架,中间件,非常非常多,在我们的项目中不可能默认配置所有的,那么对于自动配置而言的第一个问题就是满足了什么条件,才去自动配置呢?,先不管这个条件是怎么样的,假设这个条件已经得到了满足,接下来就有第二个问题应该创建哪些bean呢?,继续,假设创建哪些bean也确定了,那么,必定某些bean有一些属性值是需要动态设置的,因此,第三个问题就是bean的属性值来源在哪里?。总结如下:

满足了什么条件,才去自动配置呢?
应该创建哪些bean呢?
bean的属性值来源在哪里?

我们就从这三个问题作为入口来进行分析。

1:自动配置3个问题的解决方案

我们直接来看一个springboot中的自动配置类,然后再就着这个自动配置类来分析这三个问题的解决方案,如下是springboot关于web服务器自动配置类的源码:

@Configuration 
@ConditionalOnWebApplication // <2.1>
@EnableConfigurationProperties(ServerProperties.class) // <4.1>
public class  EmbeddedWebServerFactoryCustomizerAutoConfiguration {

	@Configuration
	@ConditionalOnClass({ Tomcat.class, UpgradeProtocol.class }) // <2.2>
	public static class TomcatWebServerFactoryCustomizerConfiguration {

		@Bean // <3.1>
		public TomcatWebServerFactoryCustomizer tomcatWebServerFactoryCustomizer(
				Environment environment, ServerProperties serverProperties) {
			
			return new TomcatWebServerFactoryCustomizer(environment, serverProperties);
		}

	}
	@Configuration
	@ConditionalOnClass({ Server.class, Loader.class, WebAppContext.class }) // <2.3>
	public static class JettyWebServerFactoryCustomizerConfiguration {

		@Bean // <3.2>
		public JettyWebServerFactoryCustomizer jettyWebServerFactoryCustomizer(
				Environment environment, ServerProperties serverProperties) { 
			return new JettyWebServerFactoryCustomizer(environment, serverProperties);
		}

	}
}

这是一个Java Config类,Java config是一种允许我们通过写Java代码创建java bean的机制,@Configuration注解说明类是一个Java Config的类,@Bean来定义一个Java bean,@Bean注解功能同xml配置方式的标签。

1.1:满足了什么条件,才去自动配置呢?

在上述源码中,我们看<2.1>,<2.2>,<2.3>处的形如@ConditionalOnxxx的注解,这种注解我们叫做条件注解,就是用来设置需要满足条件的,这种注解在springboot中非常非常的多,我们在自定义springboot的starter的时候也可能需要自定义这种注解,如下图是springboot中提供的条件注解:
springboot之自动配置原理分析_第1张图片
所以这个问题的结论就是条件注解

1.2:应该创建哪些bean呢?

在上述源码中<3.1>,<3.2>处标记有@Bean注解的方法,其返回值就是需要创建的bean。

1.3:bean的属性值来源在哪里?

属性值来源其实就是在application.yml的配置文件中,接下来看如何接收这些配置,在上述源码中<4.1>@EnableConfigurationProperties中设置了ServerProperties.class,那么就会自动创建类型为ServerProperties的bean,ServerProperties定义如下:

@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {
	private Integer port;
	private InetAddress address;
	...snip...
}

其中prefix设置要读取的配置项的前缀,然后和属性名称组合就是配置文件的key了,最终设置到对应的属性中,如配置文件中设置server.port=9999,则ServerProperties的bean的port的属性值是9999
到这里,我们知道了在springboot中的自动配置类对这3个问题,提供的解决方案,但是springboot怎么知道需要加载哪些自动配置类呢,我们继续来看。

1.4:什么是自动配置类

解决我们提出的三个问题的类,我们可以称之为自动配置类,当然这是我自己定义的,非官方,仅用于理解使用,通过自动配置类来实现在满足一定条件时,通过读取配置文件信息,来创建bean

2:自动配置类

在spring中提供了一个工具类org.springframework.core.io.support.SpringFactoriesLoader,用于从META-INF/spring.factories文件中,根据类名称查询配置的具体类,springboot通过这个机制来读取自动配置类,在spring-boot-autoconfigure项目中提供了大量的自动配置类,就是如此配置的,当然我们自己编写starter定义的自动配置类,也需要按照这种方式来定义,如下图红框中就是具体的配置:
springboot之自动配置原理分析_第2张图片
如下图是kafka和mongo的自动配置类:
springboot之自动配置原理分析_第3张图片

3:条件注解

通过前面的分析,我们知道,条件注解是用来判断是否需要加载为bean。接下来我们看下条件注解的由来。为了解决在不同的环境中注册不同的bean的需求,spring3.1版本,提供了@Profile注解,如下在不同环境注册不同bean数据源:

@Configuration
public class MyDatasourceConfiguration {
	// 开发环境的数据源bean
	@Bean
	@Profile("dev")
	public DataSource getDevDatasoucce() {
		DataSource devDatasource = ...;
		// 具体逻辑
		return devDatasource;
	}
	// 正式环境使用的数据源bean
	@Bean
	@Profile("production")
	public DataSouce getProductionDatasouce() {
		DataSource productionDatasouce = ...;
		// 具体逻辑
		return productionDatasource;
	}
}

但是只能提供基于变量spring.profiles.active判断来创建bean,因此,在spring4版本中,使用了更加通用的@Conditional注解,源码如下:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {

	Class<? extends Condition>[] value();

}

从源码中可以看到其value需要实现了org.springframework.context.annotation.Condition接口的Class类数组作为参数,该接口的作用是用来进行条件满足是和否的判断的,需要我们自己根据情况来提供具体实现,源码如下:

@FunctionalInterface
public interface Condition {
	boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}

这样创建bean的各种条件就全部支持了,springboot基于此,提供了很多常用的条件注解,以及配套的条件判断类,主要如下:

@ConditionalOnBean:当IOC容器中有某个bean的时候,满足条件
@ConditionOnMissingBean:当IOC容器中没有某个bean的时候,满足条件
@ConditionalOnSingleCandidate:当IOC容器中只有一个候选bean
,或者是有多个候选bean但是指定了首选bean,满足条件
@ConditionalOnClass:当类路径下有某个类时,满足条件
@ConditionalOnMissingClass:当类路径下缺失某个类时,满足条件
@ConditionalOnProperty:当指定的属性有指定的值时,满足条件
@ConditionalOnExpression:当SpEL表达式为true时,满足条件
@ConditionalOnJava:当前项目java版本满足指定的java版本时,满足条件
@ConditionalOnWebApplication:当前项目是web项目时,满足条件
@ConditionalOnNotWebApplication:当前项目不是web项目时,满足条件

上述@ConditionalOnSingleCandidate中涉及到了首选bean(存在多个满足要求的bean时,通过primary=true设置最高优先级,即首选bean),关于这个不清楚的可以参考这里。@ConditionalOnExpression涉及到了SpEL,关于SpEL不清楚的可以参考这里。

4:配置属性

springboot默认从application.yaml,application.properties文件中读取属性,用来填充bean的属性,也可以配合@ConditionalOnProperty条件注解来使用。

5:内置starter

什么是starter,封装某个框架所需要依赖的GAV我们就可以称之为是starter,又叫做是起步依赖,通过起步依赖就可以引入框架需要的jar,此时再配合自动配置类中的@ConditionalOnClass条件注解,就可以实现框架的自动配置,完成框架所依赖相关bean的创建工作,因此在实际使用中都是通过引入起步依赖来完成工作的,这里的起步依赖可以是springboot已经提供的常用框架,也可以是我们在工作中根据业务自定义的,如下图就是springboot提供的起步依赖(部分)
springboot之自动配置原理分析_第4张图片
当然我们也可以自定义starter,可以参考这里,这里。

6:重要的注解

6.1:SpringBootApplication

在编写springboot的main程序时,想要成为springboot程序,必定会使用该注解,用来写在main方法所在的类上,可能如下:

@SpringBootApplication
public class SpringbootHelloWorldApplication {

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

}

该注解最重要的一个作用就是开启自动配置,在META-INF/spring.factories文件中提供的的自动配置类,不管是springboot已经提供的还是自定义的都需要使用该注解对应的class名称作为key进行配置,如下是在这篇文章自定义的:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
dongshi.daddy.beanconfig.MyFirstBeanConfig

有一点不太好,就是自动配置类,没有使用AutoConfiguration结尾,大家看的时候可自行按照规范修改类名称。当然该注解还组合的其它的功能,下面看下注解的源码:

// 通过@SpringBootConfiguration,代表是一个java config类
// 通过@EnableAutoConfiguration,开启自动配置
// 通过@ComponentScan,设置扫描包路径
@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 {

	// 设置不会被自动配置的类的class数组,如果是不希望某个自动配置
	// 生效,可以设置到这里
	@AliasFor(annotation = EnableAutoConfiguration.class)
	Class<?>[] exclude() default {};

	// 设置不会被自动配的类的bean名称数组,如果不希望某个自动配置类
	// 生效,可以将其bean名称设置到这里
	@AliasFor(annotation = EnableAutoConfiguration.class)
	String[] excludeName() default {};

	@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
	String[] scanBasePackages() default {};

	@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
	Class<?>[] scanBasePackageClasses() default {};

}

下面我们分别来看下其中重要的注解。

6.2:Inherited

含义是:如果某个自定义的注解,使用了Inherited注解进行声明,则标注了该自定义注解类的子类,将会继承该自定义注解,但是需要注意仅仅对类上的声明有效,对于方法和属性上的声明无效。不了解的朋友可以参考这篇文章。

6.3:SpringBootConfiguration

该注解是java config中@Configuration注解的子注解,只是为了在springboot环境下更加见名知意,本质上和@Configuration注解没有区别,然后在类里边的方法就可以使用@Bean注解来定义spring bean了,源码如下:

// 同@Configuration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {

}

不了解的朋友可以参考这篇文章。

6.4:ComponentScan

用于指定扫描使用了@Configuration,@Component@Controller,@Service,@Repository注解的类的包路径。关于此注解不清楚的可以参考这篇文章。

6.5:EnableAutoConfiguration

这是springboot中开启自动配置的注解,为自定义注解,完整路径是org.springframework.boot.autoconfigure.EnableAutoConfiguration。源码如下:

// 开启spring应用环境的自动配置,尝试猜测并且自动配置可能需要的spring beans。自动配置类一般都是
// 基于classpath或者是你已经定义的bean来应用的。比如,如果在classpath下有tomcat-embedded.jar
// 则说明你可能需要自动创建一个TomcatServletWebServerFactory(如果没有定义ServletWebServerFactory的话)。使用了该注解,自动配置将在没有任何副作用的情况下生效。可以通过exclude(),excludeNames()来排除不想要自动配置的类,也可以使用spring.autoconfigure.exclude属性来
// 排除。自动配置永远在用户定义的bean完成注册之后生效。该注解最好使用在根目录,这样其子包也能自动被扫描到。
// 通过SpringFactoriesLoader机制来自动定位需要自动配置的bean,一般配合条件注解@ConditionalOnClass
// @ConditionalOnMissingBean等一起使用
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

	String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

	// 设置要排除的自动配置类的class类型数组,之后永远也不会被应用自动配置
	Class<?>[] exclude() default {};

	// 设置要排除的自动配置类对应的bean名称数组,之后永远也不会被应用自动配置
	String[] excludeName() default {};
}

这里会多次间接或者是直接的使用到@Import注解,该注解的作用是引入java config配置类,本质上就是引入对象到IOC容器中,成为spring bean,不了解的朋友可以参考这篇文章。
其中比较关键的注解是@AutoConfigurationPackage@Import(AutoConfigurationImportSelector.class),我们先来看第一个@AutoConfigurationPackage,源码如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {

}

其中@Import(AutoConfigurationPackages.Registrar.class)是通过引入bean definition的方式来引入对象到IOC容器中,相比普通方式省略了解析为beandefiniton的步骤,源码如下:

org.springframework.boot.autoconfigure.AutoConfigurationPackages.Registrar
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

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

继续:

org.springframework.boot.autoconfigure.AutoConfigurationPackages#register
public static void register(BeanDefinitionRegistry registry, String... packageNames) {
	if (registry.containsBeanDefinition(BEAN)) {
		...snip...
	}
	else {
		GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
		beanDefinition.setBeanClass(BasePackages.class);
		// 设置启动类所在的包路径
		beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames);
		beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
		registry.registerBeanDefinition(BEAN, beanDefinition);
	}
}

其实就是定义了用于封装启动类所在包路径的BasePackages对象的BeanDefinition对象,bean名称是private static final String BEAN = AutoConfigurationPackages.class.getName();,后续如果是需要用到启动类所在包路径的话,就可以通过该spring bean来获取了。
注解@Import(AutoConfigurationImportSelector.class)是重点,用于导入自动配置类。我们单起一部分来讲解。

6.6:AutoConfigurationImportSelector

org.springframework.boot.autoconfigure.AutoConfigurationImportSelector实现了DeferredImportSelector接口实现自动配置类的引入。这些自动配置类是通过方法org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getCandidateConfigurations获取的,先放在这里,后续会分析执行过程是如何的,方法源码如下:

org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getCandidateConfigurations
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
	// 从META-INF/spring.factories文件中获取自动配置类
	// getSpringFactoriesLoaderFactoryClass():interface org.springframework.boot.autoconfigure.EnableAutoConfiguration
	// <202105251818>
	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;
}

<202105251818>处结果如下图:
springboot之自动配置原理分析_第5张图片
下图是如何调用到这里的,发起点还是容器刷新refresh,感兴趣的可参考该图进行debug调试:
springboot之自动配置原理分析_第6张图片
其中红框的getImports方法就是我们需要进一步分析的方法,源码如下:

org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup#selectImports
// <202105281804>
public Iterable<Entry> selectImports() {
	if (this.autoConfigurationEntries.isEmpty()) {
		return Collections.emptyList();
	}
	// map(AutoConfigurationEntry::getExclusions) 获取exclusions的set集合private final Set exclusions;
	// flatMap(Collection::stream) 展开exclusions集合,并合并
	// collect(Collectors.toSet()),转换成set集合并返回
	// 总体是获取autoConfigurationEntries中所有的exlusions集合,合并到一个set集合中
	Set<String> allExclusions = this.autoConfigurationEntries.stream()
			.map(AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet());
	// 获取autoConfigurationEntries中每个元素的configurations集合,并合并到一个set集合中返回
	Set<String> processedConfigurations = this.autoConfigurationEntries.stream()
			.map(AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream)
			.collect(Collectors.toCollection(LinkedHashSet::new));
	// 从可能需要处理的的集合中删除需要排除的
	// 这里其实在前面逻辑已经排除过了,为什么要重复排除???不影响结果,忽略!!!
	processedConfigurations.removeAll(allExclusions);
	// 生成Entry的List集合返回,后续使用
	return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream()
			.map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName))
			.collect(Collectors.toList());
}

<202105281804>处方法使用到了java8 Stream API,不清楚的可以参考这篇文章。
我们接着来看在getImports方法之前被调用的方法process,源码如下:

org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup#process
// annotationMetadata:是启动类上的注解元信息对象,一般只有一个@SpringBootApplication注解
// deferredImportSelector:通过@Import注解配置的AutoConfigurationImportSelector,是@SpringBootApplication注解的组合注解,用于完成导入自动配置类的工作
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
	Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
			() -> String.format("Only %s implementations are supported, got %s",
					AutoConfigurationImportSelector.class.getSimpleName(),
					deferredImportSelector.getClass().getName()));
	// <202105271135>		
	AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
			.getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);
	// 添加到autoConfigurationEntries集合中
	// private final List autoConfigurationEntries = new ArrayList<>();
	this.autoConfigurationEntries.add(autoConfigurationEntry);
	for (String importClassName : autoConfigurationEntry.getConfigurations()) {
		// private final Map entries = new LinkedHashMap<>();
		// key是要导入的自动配置类的全限定类名称,value是启动类的注解元信息对象
		this.entries.putIfAbsent(importClassName, annotationMetadata);
	}
}

<202105271135>处主要是两个方法调用,一个是org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup#getAutoConfigurationMetadata,另一个是org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getAutoConfigurationEntry,第一个方法是用来获取自动配置类和条件注解的组合信息的,第二个方法是用来获取需要自动配置的类和不需要自动配置的类,然后封装在AutoConfigurationEntry对象中返回,分别来看下。第一个方法参考6.6.1:getAutoConfigurationMetadata,第二个方法参考6.6.2:getAutoConfigurationEntry

6.6.1:getAutoConfigurationMetadata

源码如下:

org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup#getAutoConfigurationMetadata
private AutoConfigurationMetadata getAutoConfigurationMetadata() {
	// 为null才重新获取
	if (this.autoConfigurationMetadata == null) {
		// <202105271315>
		this.autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
	}
	return this.autoConfigurationMetadata;
}

<202105271315>处是从spring-boot-autoconfigure-2.1.14.RELEASE.jar!\META-INF\spring-autoconfigure-metadata.properties文件读取配置的信息,配置到配置文件中的原因是,在不加载自动配置类信息的前提下获取自动配置和其条件注解信息,省去了加载自动配置类信息的过程,提高程序启动的效率。

关于加载过程详细分析,参考这篇文章。

源码如下:

org.springframework.boot.autoconfigure.AutoConfigurationMetadataLoader#loadMetadata(java.lang.ClassLoader)
public static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
	// protected static final String PATH = "META-INF/" + "spring-autoconfigure-metadata.properties";
	// <202105271319>
	return loadMetadata(classLoader, PATH);
}

<202105271319>处源码如下:

org.springframework.boot.autoconfigure.AutoConfigurationMetadataLoader#loadMetadata(java.lang.ClassLoader, java.lang.String)
static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) {
	try {
		Enumeration<URL> urls = (classLoader != null) ? classLoader.getResources(path)
				: ClassLoader.getSystemResources(path);
		Properties properties = new Properties();
		while (urls.hasMoreElements()) {
			properties.putAll(PropertiesLoaderUtils.loadProperties(new UrlResource(urls.nextElement())));
		}
		// 该处源码如下,直接封装为AutoConfigurationMetadata对象,并返回
		/*
		staticAutoConfigurationMetadata loadMetadata(Properties properties) {
			return new PropertiesAutoConfigurationMetadata(properties);
		}
		*/
		// <202105271328>
		return loadMetadata(properties);
	}
	catch (IOException ex) {
		throw new IllegalArgumentException("Unable to load @ConditionalOnClass location [" + path + "]", ex);
	}
}

<202105271328>处最终结果,如下图:
springboot之自动配置原理分析_第7张图片
这些信息在后续过滤自动配置类的时候会使用到。再多说一点,这里的key是自动配置类的全限定名称+条件注解名称组合的字符串,value是条件注解的值。

6.6.2:getAutoConfigurationEntry

该方法用于获取需要自动配置的类和不需要自动配置的类,封装在AutoConfigurationEntry对象中,调用的方法是org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getAutoConfigurationEntry,源码如下:

org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getAutoConfigurationEntry
// 根据启动类上的@SpringBootAppplication
// 注解组合的@Import(AutoConfigurationImportSelector.class)注解来完成引入自动配置类的工作
// 参数autoConfigurationMetadata:封装自动配置类和其条件注解元信息组合信息的对象
// 参数annotationMetadata:启动类的注解元信息对象
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
		AnnotationMetadata annotationMetadata) {
	// <202105271344>
	// 判断是否启用了自动配置,如果没有则返回空
	if (!isEnabled(annotationMetadata)) {
		return EMPTY_ENTRY;
	}
	// 获取启动类注解元信息的属性信息
	AnnotationAttributes attributes = getAttributes(annotationMetadata);
	// <202105271434>
	List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
	// 删除重复的,源码如下:
	/*
	org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#removeDuplicates
	protected final  List removeDuplicates(List list) {
		return new ArrayList<>(new LinkedHashSet<>(list));
	}
	*/
	// 简单粗暴
	configurations = removeDuplicates(configurations);
	// 获取需要排除的自动配置类,即在注解中显式设置的需要排除的自动配置类
	// <202105271525>
	Set<String> exclusions = getExclusions(annotationMetadata, attributes);
	// <202105271600>
	// 检测排除的自动配置类是否合法
	checkExcludedClasses(configurations, exclusions);
	// 从自动配置类中移除需要排除的自动配置类
	configurations.removeAll(exclusions);
	// <202105271618>
	configurations = filter(configurations, autoConfigurationMetadata);
	// 触发自动配置类导入事件
	// <202105271822>
	fireAutoConfigurationImportEvents(configurations, exclusions);
	// 需要加载的自动配置类configurations和需要排除的自动配置类exclusions作为参数
	// 构造AutoConfigurationEntry对象
	return new AutoConfigurationEntry(configurations, exclusions);
}

<202105271344>处是获取是否启用自动配置功能,源码如下:

org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#isEnabled
protected boolean isEnabled(AnnotationMetadata metadata) {
	if (getClass() == AutoConfigurationImportSelector.class) {
		// 从Environment中获取String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
		// 的值,true则正常执行自动配置,false,则不执行自动配置
		return getEnvironment().getProperty(EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class, true);
	}
	return true;
}

<202105271434>处是获取候选的待自动配置的类,其实前面已经大概分析过,这里再贴下源码:

org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#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;
}

<202105271525>处是获取需要排除的自动配置类,源码如下:

org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getExclusions
protected Set<String> getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) {
	Set<String> excluded = new LinkedHashSet<>();
	// 获取exclude配置的值
	excluded.addAll(asList(attributes, "exclude"));
	// 获取excludeName配置的值
	excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName")));
	// 获取环境变量spring.autoconfigure.exclude配置的排除信息
	// String[] excludes = getEnvironment().getProperty(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE, String[].class);
	excluded.addAll(getExcludeAutoConfigurationsProperty());
	return excluded;
}

如下图是启动类配置和attribute,以及excluded集合的对比注意:仅仅是例子,因为配置的实际上不是自动配置类,所以后续在checkExcludedClasses方法会抛出异常
springboot之自动配置原理分析_第8张图片
<202105271600>处是检测排除的自动配置类是否合法,源码如下:

org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#checkExcludedClasses
private void checkExcludedClasses(List<String> configurations, Set<String> exclusions) {
	List<String> invalidExcludes = new ArrayList<>(exclusions.size());
	for (String exclusion : exclusions) {
		// 如果是classpath下有该类,并且不是自动配置类,则添加到无效排除
		// invalidExcludes集合中
		if (ClassUtils.isPresent(exclusion, getClass().getClassLoader()) && !configurations.contains(exclusion)) {
			invalidExcludes.add(exclusion);
		}
	}
	// 如果是无效排除集合invalidExcludes集合不为空
	if (!invalidExcludes.isEmpty()) {
		// 抛出异常并给出不合法的自动配置类信息列表,源码如下:
		/*
		protected void handleInvalidExcludes(List invalidExcludes) {
			StringBuilder message = new StringBuilder();
			for (String exclude : invalidExcludes) {
				message.append("\t- ").append(exclude).append(String.format("%n"));
			}
			throw new IllegalStateException(String.format(
					"The following classes could not be excluded because they are" + " not auto-configuration classes:%n%s",
					message));
		}
		*/
		handleInvalidExcludes(invalidExcludes);
	}
}

<202105271618>处是根据自动配置类的条件注解信息进行过滤操作,因为不满足条件注解的肯定是不需要自动配置的,是吧?,具体过程参考这篇文章。
<202105271822>处是在获取了要引入的自动配置类后触发事件,源码如下:

org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#fireAutoConfigurationImportEvents
private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) {
	// 从META-INF/spring.factories中获取key为AutoConfigurationImportListener的AutoConfigurationImportListener的实现类们,源码如下:
	/*
	protected List getAutoConfigurationImportListeners() {
		return SpringFactoriesLoader.loadFactories(AutoConfigurationImportListener.class, this.beanClassLoader);
	}
	*/
	List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners();
	// 调用监听器,执行监听器逻辑
	if (!listeners.isEmpty()) {
		AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);
		for (AutoConfigurationImportListener listener : listeners) {
			invokeAwareMethods(listener);
			listener.onAutoConfigurationImportEvent(event);
		}
	}
}

你可能感兴趣的:(springboot,java,spring,spring,boot)