介绍

构建Springboot项目时我们会创建一个启动类

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

1. 在之前使用Spring框架时,我们一般会创建web.xml和spring-context.xml等文件配置组件扫描、调度器、视图解析器等。

2. 而在SpringBoot中则简单了很多,这里就有自动配置发挥作用。如默认用的内嵌式容器是 Tomcat ,端口默认设置为 8080

正式进入流程解析

首先看@SpringBootApplication这个注解

@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) })
@ConfigurationPropertiesScan
public @interface SpringBootApplication { }
@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 {}; }

@EnableAutoConfiguration的作用是开启自动配置

核心是@EnableAutoConfiguration注解

这就是Spring Boot自动配置实现的核心入口,重头戏是@Import注解,这个注解导入了AutoConfigurationImportSelector类,利用AutoConfigurationImportSelector(自动配置导入选择器)将所有符合自动装配条件的bean注入到IOC容器中

查看AutoConfigurationImportSelector类代码,重头戏是selectImports方法

selectImports何时被执行

Springboot应用启动过程中使用ConfigurationClassParser分析配置类时,发现注解中存在@Import(ImportSelector)的情况。

就会创建一个相应的ImportSelector对象, 并调用其方法 public String[] selectImports(AnnotationMetadata annotationMetadata)

selectImports()方法流程

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered { // 省略。。 @Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
// 1 判断是否开启自动配置,为false直接返回空字符数组
     if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
     // 2 loadMetadata()去加载Spring预先定义的自动配置的依赖信息,下面会具体说明
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader .loadMetadata(this.beanClassLoader);
     // 3 该方法返回的就是配置项信息,下面会具体说明
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
// 省略。。。
}

2. loadMetadata()去加载Spring预先定义的自动配置的依赖信息

 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);
}
}

path的地址是

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

里面的自动配置文件(部分)

org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration.AutoConfigureBefore=org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration.ConditionalOnClass=javax.sql.DataSource,org.springframework.batch.core.launch.JobLauncher
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration.AutoConfigureAfter=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration

  2.1 根据path获取文件的URL对象

  2.2 遍历URL节点,将spring-autoconfigure-metadata.properties中的依赖信息加载到properties对象中

  2.3 调用loadMetadata()方法把properties存储到PropertiesAutoConfigurationMetadata类中

3. 在这里面会调用一个方法getAutoConfigurationEntry(),该方法返回的就是配置项信息,进入这个方法

 protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
// 3.1
3 if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
// 3.2
6 AnnotationAttributes attributes = getAttributes(annotationMetadata);
      // 3.3
7 List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 3.4
8 configurations = removeDuplicates(configurations);
// 3.5
9 Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
       // 3.6
fireAutoConfigurationImportEvents(configurations, exclusions);
       // 3.7
return new AutoConfigurationEntry(configurations, exclusions);
}

  3.1 判断是否开启自动配置

  3.2 getAttributes()方法获取@SpringBootApplication(主要是为了获取@EnableAutoConfiguation)注解上的属性

  3.3 加载META-INF/spring.factories文件, 这个文件配置了具有哪些自动配置类,文件内容如下 ,具体代码如下

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;
}

    3.3.1 getSpringFactoriesLoaderFactoryClass方法返回EnableAutoConfiguration.class

    3.3.2 SpringFactoriesLoader.loadFactoryNames会加载META-INF/spring.factories中EnableAutoConfiguration的值中定义的jar包,并将其封装到一个List中返回

  3.4 removeDuplicates() 用LinkedHashSet去重,在包装成ArrayList

  3.5 getExclusions()、checkExcludedClasses()、removeAll()、filter() 过滤注解中要排除的自动配置

  3.6 fireAutoConfigurationImportEvents() 将自动配置导入监听

  3.7 返回配置的AutoConfigurationEntry包装类

自动配置原理(简化版)

通过 @EnableAutoConfiguration 核心注解初始化,并扫描 ClassPath 目录中自动配置类。根据项目中需要添加的默认配置,如springMVC,就按一定规则获取默认配置并自动初始化所需要的 Bean。

1. 核心是@EnableAutoConfiguration
2. 里面会通过@Import注解导入(AutoConfigurationImportSelector.class)类
3. 调用importSelect方法,进行自动配置

  3.1 判断是否开启了自动配置
  3.2 getAutoConfigurationEntry方法会使用
    SpringFactoriesLoader.loadFactoryNames方法加载classpath中spring.factories文件中EnableAutoConfiguration的配置类
  3.3 经过去重,排除后会把这些配置导入监听

重点关注

@EnableAutoConfiguration

@Import

AutoConfigurationImportSelector类

selectImports方法

SpringFactoriesLoader.loadFactoryNames() 核心方法读取 ClassPath 目录下面的 META-INF/spring.factories 文件。

spring.factories 文件中存放springBoot自动配置类, 如Servlet、jpa

【示例】(JDBC)的DataSourceAutoConfiguration配置

在spring.factories中org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguratio

作用是:SpringBoot 自动配置DataSource

// 表示这是一个配置类
@Configuration(proxyBeanMethods = false)
// 判断当前项目classpath有没有这两个类 DataSource EmbeddedDatabaseType
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
// 将配置文件中对应的值和 DataSourceProperties 绑定起来,并把DataSourceProperties的属性值放到ioc容器中
@EnableConfigurationProperties(DataSourceProperties.class)
// 导入数据源注册类、数据源初始化类
@Import({ DataSourcePoolMetadataProvidersConfiguration.class, DataSourceInitializationConfiguration.class })
public class DataSourceAutoConfiguration {
  // 省略。。
}

查看DataSourceProperties属性,提供的这些属性,就可以获取到在application.yml中提供的值来设置数据源了

@ConfigurationProperties(prefix = "spring.datasource")
public class DataSourceProperties implements BeanClassLoaderAware, InitializingBean { private ClassLoader classLoader; /**
* Name of the datasource. Default to "testdb" when using an embedded database.
*/
private String name; /**
* Whether to generate a random datasource name.
*/
private boolean generateUniqueName; /**
* Fully qualified name of the connection pool implementation to use. By default, it
* is auto-detected from the classpath.
*/
private Class<? extends DataSource> type; /**
* Fully qualified name of the JDBC driver. Auto-detected based on the URL by default.
*/
private String driverClassName; /**
* JDBC URL of the database.
*/
private String url; /**
* Login username of the database.
*/
private String username; /**
* Login password of the database.
*/
private String password; /**
* JNDI location of the datasource. Class, url, username & password are ignored when
* set.
*/
private String jndiName; 。。。省略
}

总结

Spring Boot 内部提供了很多自动化配置的类, 配置类会更加classpath下是否又相关依赖类,来判断是否要配置字节,所以当我们在pom中引入了相关依赖,就可以自动配置了

 

======  【多学一点,for Better】======

SpringBoot自动配置原理学习的更多相关文章

  1. 这样讲 SpringBoot 自动配置原理,你应该能明白了吧

    https://juejin.im/post/5ce5effb6fb9a07f0b039a14 前言 小伙伴们是否想起曾经被 SSM 整合支配的恐惧?相信很多小伙伴都是有过这样的经历的,一大堆配置问题 ...

  2. springboot自动配置原理以及手动实现配置类

    springboot自动配置原理以及手动实现配置类 1.原理 spring有一个思想是"约定大于配置". 配置类自动配置可以帮助开发人员更加专注于业务逻辑开发,springboot ...

  3. SpringBoot自动配置原理

    前言 只有光头才能变强. 文本已收录至我的GitHub仓库,欢迎Star:https://github.com/ZhongFuCheng3y/3y 回顾前面Spring的文章(以学习的顺序排好): S ...

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

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

  5. 3. SpringBoot ——自动配置原理浅析

    SpringBoot的功能之所以强大,离不开它的自动配置这一大特色.但估计很多人只是知其然而不知其所以然.下面本人对自动配置原理做一个分析: 在使用SpringBoot时我们通过引入不同的Starte ...

  6. 浅谈springboot自动配置原理

    前言 springboot自动配置关键在于@SpringBootApplication注解,启动类之所以作为项目启动的入口,也是因为该注解,下面浅谈下这个注解的作用和实现原理 @SpringBootA ...

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

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

  8. springBoot 自动配置原理--自己新建一个 starter

    上篇我们说到 springboot 和 SSM 框架的区别,今天我们就看看 springboot 到底为我们做了哪些事情,让我们开发变得如此简单. springboot 中起着重要作用的是 start ...

  9. springBoot 自动配置原理

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

随机推荐

  1. 五.python小数据池,代码块的最详细、深入剖析

    一,id,is,== 在Python中,id是什么?id是内存地址,那就有人问了,什么是内存地址呢? 你只要创建一个数据(对象)那么都会在内存中开辟一个空间,将这个数据临时加在到内存中,那么这个空间是 ...

  2. NOIP 2018 考前须知

    Day0Day0Day0来水一发 Created with Raphaël 2.2.0开始考试浏览题面(3遍),注意数据范围初步判定难度,先易后难15分钟左右想正解实在想吃不出写暴力,NOIP部分分很 ...

  3. JPA EnableJpaAuditing 审计功能

    关于自动填充或更新实体中的 CreateDate.CreatedBy 等在之前有一篇 jeecg 默认为空的字段值是如何被填充的? 有提到通过拦截器的方式实现,但是今天带大家了解一下如果使用 JPA ...

  4. know thself

  5. HTML5 离线应用

    一.离线应用cache manifes文件 HTML5中构建了一个离线(无网络状态)应用,只需创建一个cache manifest文件 可以配置需要的缓存的资源,网络无连接应用任然可以使用,本地读取缓 ...

  6. 【JZOJ6223】【20190617】互膜

    题目 小\(A\)和小\(B\)在一个长度为\(2n\)的数组上面博弈,初始时奇数位置为A,偶数位置为B 小\(A\)先手,第\(i\)次操作的人可以将\(i\)或者\(i+1\)位置的值反转(也可以 ...

  7. Loj刷题记录

    又是一年云参营. 所以一起刷省选题吧. LOJ2028 「SHOI2016」随机序列 题目链接. 简要社论 发现+和-可以互相抵消,于是有贡献的时候一段前缀的乘积.设\(s[i]=\prod_{j=1 ...

  8. 什么是 Kafka Rebalance 以及关于 Rebalance Kafka-Python 社区客户端应该关注的地方

    什么是 Rebalance? Rebalance 为什么会发生?Rebalance 的情况下 consumer 是否还能正确消费消息呢? 记得之前在一段时间密集面试的时候总会问候选人这些问题. 重平衡 ...

  9. Handsontable vue如何实现在线编辑excal

    官网地址:https://handsontable.com/ 1.实现效果 2.安装 import { HotTable } from '@handsontable/vue' import Hands ...

  10. EasyEarth三维可视化解决方案——智慧林业

    智慧林业 智能巡管监护 护林员信息查询 护林员管护范围查询 护林员报警.采集数据查看 样点.样线管理 其它功能模块 ●一键考勤打卡 ●面积测量 ●任务公告发布 ●实时电量监控 ●一键报警功能 ●北斗短 ...