Spring Boot 相对于传统的Spring引入了自动配置功能,简化了项目中繁琐的配置,让开发者利用起来更加的简便、快捷。比如内嵌的tomcat容器等,这些都属于Spring Boot自动配置的范畴。

  其中@EnableAutoConfiguration注解下的AutoConfigurationImportSelector类就是自动装配的核心。在2.0.6.RELEASE版本中没有getAutoConfigurationEntry方法,在2.1.4.RELEASE版本对其做了优化变成了我们现在看到的样子。

@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(
autoConfigurationMetadata, annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
} /**
* Return the {@link AutoConfigurationEntry} based on the {@link AnnotationMetadata}
* of the importing {@link Configuration @Configuration} class.
* @param autoConfigurationMetadata the auto-configuration metadata
* @param annotationMetadata the annotation metadata of the configuration class
* @return the auto-configurations that should be imported 应该导入的自动配置
*/
protected AutoConfigurationEntry getAutoConfigurationEntry(
AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
configurations = removeDuplicates(configurations);
//获取限制候选配置的所有排除项(找到不希望自动装配的配置类)
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
//对参数exclusions进行验证,exclusion必须为自动装配的类,否则抛出异常
checkExcludedClasses(configurations, exclusions);
//移除exclusions
configurations.removeAll(exclusions);
//根据maven导入的启动器过滤出 需要导入的配置类
configurations = filter(configurations, autoConfigurationMetadata);
//配置监听事件
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}

1、其中AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader),会加载META-INF/spring-autoconfigure-metadata.properties下的所有配置信息。

final class AutoConfigurationMetadataLoader {

    protected static final String PATH = "META-INF/spring-autoconfigure-metadata.properties";

    private AutoConfigurationMetadataLoader() {
} public static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
return loadMetadata(classLoader, PATH);
} 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())));
}
return loadMetadata(properties);
}
catch (IOException ex) {
throw new IllegalArgumentException(
"Unable to load @ConditionalOnClass location [" + path + "]", ex);
}
}
}

2. getCandidateConfigurations(annotationMetadata,attributes),会加载所有包下META-INF/spring.factories的信息并组装成Map,然后读取key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的数组,并将这个数组返回。

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;
}
# Auto configure从spring.factories中读取到的文件内容
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\

3. removeDuplicates(configurations),去除重复的配置类,若我们自己写的starter 可能存主重复的。

protected final <T> List<T> removeDuplicates(List<T> list) {
return new ArrayList<>(new LinkedHashSet<>(list));
}

4. getExclusions(annotationMetadata, attributes),获取限制候选配置的所有排除项(找到不希望自动装配的配置类)。

/**
* Return any exclusions that limit the candidate configurations.
* @param metadata the source metadata
* @param attributes the {@link #getAttributes(AnnotationMetadata) annotation
* attributes}
* @return exclusions or an empty set
*/
protected Set<String> getExclusions(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
Set<String> excluded = new LinkedHashSet<>();
excluded.addAll(asList(attributes, "exclude"));
excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName")));
excluded.addAll(getExcludeAutoConfigurationsProperty());
return excluded;
} private List<String> getExcludeAutoConfigurationsProperty() {
if (getEnvironment() instanceof ConfigurableEnvironment) {
Binder binder = Binder.get(getEnvironment());
return binder.bind(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE, String[].class)
.map(Arrays::asList).orElse(Collections.emptyList());
}
String[] excludes = getEnvironment()
.getProperty(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE, String[].class);
return (excludes != null) ? Arrays.asList(excludes) : Collections.emptyList();
}

5.checkExcludedClasses(configurations, exclusions),对参数exclusions进行验证,exclusion必须为自动装配的类,否则抛出异常。

private void checkExcludedClasses(List<String> configurations,
Set<String> exclusions) {
List<String> invalidExcludes = new ArrayList<>(exclusions.size());
for (String exclusion : exclusions) {
if (ClassUtils.isPresent(exclusion, getClass().getClassLoader())
&& !configurations.contains(exclusion)) {
invalidExcludes.add(exclusion);
}
}
if (!invalidExcludes.isEmpty()) {
handleInvalidExcludes(invalidExcludes);
}
} /**
* Handle any invalid excludes that have been specified.
* @param invalidExcludes the list of invalid excludes (will always have at least one
* element)
*/
protected void handleInvalidExcludes(List<String> 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));
}

6. filter(configurations, autoConfigurationMetadata),根据项目中的AutoConfigurationImportFilter类进行过滤
在项目中找到所有AutoConfigurationImportFilter类进行过滤,对于自动配置类,只要其不满足任意一个filter的match方法,就将其进行过滤,不再自动配置。

private List<String> filter(List<String> configurations,
AutoConfigurationMetadata autoConfigurationMetadata) {
long startTime = System.nanoTime();
String[] candidates = StringUtils.toStringArray(configurations);
boolean[] skip = new boolean[candidates.length];
boolean skipped = false;
for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {
invokeAwareMethods(filter);
boolean[] match = filter.match(candidates, autoConfigurationMetadata);
for (int i = 0; i < match.length; i++) {
if (!match[i]) {
skip[i] = true;
candidates[i] = null;
skipped = true;
}
}
}
if (!skipped) {
return configurations;
}
List<String> result = new ArrayList<>(candidates.length);
for (int i = 0; i < candidates.length; i++) {
if (!skip[i]) {
result.add(candidates[i]);
}
}
if (logger.isTraceEnabled()) {
int numberFiltered = configurations.size() - result.size();
logger.trace("Filtered " + numberFiltered + " auto configuration class in "
+ TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime)
+ " ms");
}
return new ArrayList<>(result);
}

至此,自动配置的逻辑实现。我们通过RedisAutoConfiguration为例,完整的展示一遍整个流程:

@Configuration //标识是一个配置类
@ConditionalOnClass(RedisOperations.class) //判断环境中是否有这个类
@EnableConfigurationProperties(RedisProperties.class) //启动指定类的配置功能,并且把配置文件中的属性和RedisProperties关联
@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;
} @Bean
@ConditionalOnMissingBean
public StringRedisTemplate stringRedisTemplate(
RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;

具体的实现流程如图:

# Auto Configure

Springboot 自动装配置的更多相关文章

  1. CAS学习笔记五:SpringBoot自动/手动配置方式集成CAS单点登出

    本文目标 基于SpringBoot + Maven 分别使用自动配置与手动配置过滤器方式实现CAS客户端登出及单点登出. 本文基于<CAS学习笔记三:SpringBoot自动/手动配置方式集成C ...

  2. 助力SpringBoot自动配置的条件注解ConditionalOnXXX分析--SpringBoot源码(三)

    注:该源码分析对应SpringBoot版本为2.1.0.RELEASE 1 前言 本篇接 如何分析SpringBoot源码模块及结构?--SpringBoot源码(二) 上一篇分析了SpringBoo ...

  3. SpringBoot入门教程(三)通过properties实现多个数据库环境自动切换配置

    前面的文章已经介绍了CentOS部署SpringBoot项目从0到1的详细过程,包括Linux安装ftp.Tomcat以及Java jdk的全部过程.这篇文章主要介绍关于springboot如何通过多 ...

  4. SpringBoot系列二:SpringBoot自动配置原理

    主程序类的注解 @SpringBootApplication 注解,它其实是个组合注解,源码如下: @Target({ElementType.TYPE}) @Retention(RetentionPo ...

  5. SpringBoot自动配置源码调试

    之前对SpringBoot的自动配置原理进行了较为详细的介绍(https://www.cnblogs.com/stm32stm32/p/10560933.html),接下来就对自动配置进行源码调试,探 ...

  6. SpringBoot实战之SpringBoot自动配置原理

    SpringBoot 自动配置主要通过 @EnableAutoConfiguration, @Conditional, @EnableConfigurationProperties 或者 @Confi ...

  7. springBoot 自动配置原理

    在之前文章中说过,springBoot会根据jar包去添加许多的自动配置,本文就来说说为什么会自动配置,自动配置的原理时什么? springBoot在运行SpringApplication对象实例化时 ...

  8. springboot自动配置源码解析

    springboot版本:2.1.6.RELEASE SpringBoot 自动配置主要通过 @EnableAutoConfiguration, @Conditional, @EnableConfig ...

  9. 源码学习系列之SpringBoot自动配置(篇一)

    源码学习系列之SpringBoot自动配置源码学习(篇一) ok,本博客尝试跟一下Springboot的自动配置源码,做一下笔记记录,自动配置是Springboot的一个很关键的特性,也容易被忽略的属 ...

随机推荐

  1. STM32F429的特点

    STM32F429 内核 Crotex M4 最高主频 180MHZ FPU 有 DSP指令集 有 最大SRAM 256K 备份域SRAM 有 最大FLASH 2MB GPIO最高翻转速度 90MHZ ...

  2. 加深对于 MVC、MVP、MVVM 的概念理解

    目录 MVC 对 MVC 的误解及缘由 MVP MVVM MVC MVC - 维基百科,自由的百科全书 MVC 是软件工程的一种软件架构模式,它不是具体的技术,而是一种代码分层的理念,主要体现了职责分 ...

  3. 从头学pytorch(二十):残差网络resnet

    残差网络ResNet resnet是何凯明大神在2015年提出的.并且获得了当年的ImageNet比赛的冠军. 残差网络具有里程碑的意义,为以后的网络设计提出了一个新的思路. googlenet的思路 ...

  4. jQuery, 文本框获得焦点后, placeholder提示文字消失

    文本框获得焦点后, 提示文字消失, 基于jQuery, 兼容性: html5 //所有文本框获得焦点后, 提示文字消失 $('body').on('focus', 'input[placeholder ...

  5. postgresql gin索引使用

    由于属于老项目,postgresql使用版本9.6,主要解决‘%name%"查询无法使用索引问题.pg_trgm模块提供函数和操作符测定字母,数字,文本基于三元模型匹配的相似性, 还有支持快 ...

  6. Mysql的binlog日志与mysqlbinlog命令

    binlog相关 MySQL 的二进制日志 binlog 可以说是 MySQL 最重要的日志,它记录了所有的 DDL 和 DML 语句(除了数据查询语句select.show等),以事件形式记录,还包 ...

  7. ForkJoin统计文件夹中包含关键词的数量

    2018-06-09总结: ForkJoin确实可以很快速的去解析文件并统计关键词的数量,但是如果文件过大就会出现内存溢出,是否可以通过虚拟内存方式解决内存溢出的问题呢? package com.ox ...

  8. [bzoj4447] [loj#2010] [Scoi2015] 小凸解密码

    Description 小凸得到了一个密码盘,密码盘被等分成 \(N\) 个扇形,每个扇形上有一个数字(0-9),和一个符号("+"或"*") 密码盘解密的方法 ...

  9. Windows和Linux换行的区别

    Linux下的换行符是: "\n" Windows的换行符 : "\r\n" r = return n = newline TODO 原因: 根据平台自己转化的 ...

  10. 小白学Java:内部类

    目录 小白学Java:内部类 内部类的分类 成员内部类 局部内部类 静态内部类 匿名内部类 内部类的继承 内部类有啥用 小白学Java:内部类 内部类是封装的一种形式,是定义在类或接口中的类. 内部类 ...