在Java中各种框架,中间件,非常非常多,在我们的项目中不可能默认配置所有的,那么对于自动配置而言的第一个问题就是满足了什么条件,才去自动配置呢?
,先不管这个条件是怎么样的,假设这个条件已经得到了满足,接下来就有第二个问题应该创建哪些bean呢?
,继续,假设创建哪些bean也确定了,那么,必定某些bean有一些属性值是需要动态设置的,因此,第三个问题就是bean的属性值来源在哪里?
。总结如下:
满足了什么条件,才去自动配置呢?
应该创建哪些bean呢?
bean的属性值来源在哪里?
我们就从这三个问题作为入口来进行分析。
我们直接来看一个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配置方式的
标签。
在上述源码中,我们看<2.1>
,<2.2>
,<2.3>
处的形如@ConditionalOnxxx
的注解,这种注解我们叫做条件注解
,就是用来设置需要满足条件的,这种注解在springboot中非常非常的多,我们在自定义springboot的starter的时候也可能需要自定义这种注解,如下图是springboot中提供的条件注解:
所以这个问题的结论就是条件注解
。
在上述源码中<3.1>
,<3.2>
处标记有@Bean
注解的方法,其返回值就是需要创建的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怎么知道需要加载哪些自动配置类呢,我们继续来看。
解决我们提出的三个问题的类,我们可以称之为自动配置类
,当然这是我自己定义的,非官方,仅用于理解使用,通过自动配置类来实现在满足一定条件时,通过读取配置文件信息,来创建bean
。
在spring中提供了一个工具类org.springframework.core.io.support.SpringFactoriesLoader
,用于从META-INF/spring.factories
文件中,根据类名称查询配置的具体类,springboot通过这个机制来读取自动配置类,在spring-boot-autoconfigure
项目中提供了大量的自动配置类,就是如此配置的,当然我们自己编写starter定义的自动配置类,也需要按照这种方式来定义,如下图红框中就是具体的配置:
如下图是kafka和mongo的自动配置类:
通过前面的分析,我们知道,条件注解是用来判断是否需要加载为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不清楚的可以参考这里。
springboot默认从application.yaml,application.properties文件中读取属性,用来填充bean的属性,也可以配合@ConditionalOnProperty条件注解来使用。
什么是starter,封装某个框架所需要依赖的GAV
我们就可以称之为是starter,又叫做是起步依赖,通过起步依赖就可以引入框架需要的jar,此时再配合自动配置类中的@ConditionalOnClass
条件注解,就可以实现框架的自动配置,完成框架所依赖相关bean的创建工作,因此在实际使用中都是通过引入起步依赖来完成工作的,这里的起步依赖可以是springboot已经提供的常用框架,也可以是我们在工作中根据业务自定义的,如下图就是springboot提供的起步依赖(部分)
:
当然我们也可以自定义starter,可以参考这里,这里。
在编写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 {};
}
下面我们分别来看下其中重要的注解。
含义是:如果某个自定义的注解,使用了Inherited注解进行声明,则标注了该自定义注解类的子类,将会继承该自定义注解,但是需要注意仅仅对类上的声明有效,对于方法和属性上的声明无效。不了解的朋友可以参考这篇文章。
该注解是java config中@Configuration
注解的子注解,只是为了在springboot环境下更加见名知意,本质上和@Configuration
注解没有区别,然后在类里边的方法就可以使用@Bean
注解来定义spring bean了,源码如下:
// 同@Configuration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}
不了解的朋友可以参考这篇文章。
用于指定扫描使用了@Configuration
,@Component
,@Controller
,@Service
,@Repository
注解的类的包路径。关于此注解不清楚的可以参考这篇文章。
这是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)
是重点,用于导入自动配置类。我们单起一部分来讲解。
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>
处结果如下图:
下图是如何调用到这里的,发起点还是容器刷新refresh,感兴趣的可参考该图进行debug调试:
其中红框的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
。
源码如下:
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>
处最终结果,如下图:
这些信息在后续过滤自动配置类的时候会使用到。再多说一点,这里的key是自动配置类的全限定名称+条件注解名称
组合的字符串,value是条件注解的值。
该方法用于获取需要自动配置的类和不需要自动配置的类,封装在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方法会抛出异常
:
<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);
}
}
}