SpringBoot自动配置原理分析

目录
  • 前言
  • 一、启动类
    • 1.1、@SpringBootConfiguration
    • 1.2、@EnableAutoConfiguration
    • 1.3、@ComponentScan
    • 1.4、探究方向
  • 二、@SpringBootConfiguration
  • 三、@EnableAutoConfiguration
    • 3.1、@AutoConfigurationPackage
    • 3.2、@Import(AutoConfigurationImportSelector.class)
      • 3.2.1、getCandidateConfigurations()
      • 3.2.2、loadSpringFactories()
  • 四、小结

前言

SpringBoot是我们经常使用的框架,那么你能不能针对SpringBoot实现自动配置做一个详细的介绍。如果可以的话,能不能画一下实现自动配置的流程图。牵扯到哪些关键类,以及哪些关键点。

下面我们一起来看看吧!!

阅读完本文:

  • 你能知道 SpringBoot 启动时的自动配置的原理知识
  • 你能知道 SpringBoot 启动时的自动配置的流程
  • 以及对于 SpringBoot 一些常用注解的了解

一步一步 debug 从浅到深。

注意本文的 SpringBoot 版本为 2.5.2

一、启动类

前言什么的,就不说了,大家都会用的,我们直接从 SpringBoot 启动类说起。

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

@SpringBootApplication 标注在某个类上说明这个类是 SpringBoot 的主配置类, SpringBoot 就应该运行这个类的main方法来启动 SpringBoot 应用;是我们研究的重点!!!它的本质是一个组合注解,我们点进去,看看javadoc上是怎么写的,分析从浅到深,从粗略到详细。

我们点进去看:

@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {}

Javadoc上是这么写的

表示声明一个或多个@Bean方法并触发 auto-configuration 和 component scanning 的 configuration 类。 这是一个方便的注解,相当于声明了 @Configuration 、 @EnableAutoConfiguration 和@ComponentScan 。

---为什么它能集成这么多的注解的功能呢?

是在于它上面的 @Inherited 注解, @Inherited 表示自动继承注解类型。

这里的最重要的两个注解是 @SpringBootConfiguration 和 @EnableAutoConfiguration

1.1、@SpringBootConfiguration

我们先点进去看看 @SpringBootConfiguration注解:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {}。

1.2、@EnableAutoConfiguration

再看看 @EnableAutoConfiguration.

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {}

1.3、@ComponentScan

@ComponentScan:配置用于 Configuration 类的组件扫描指令。 提供与 Spring XML 的 <context:component-scan> 元素并行的支持。 可以 basePackageClasses 或basePackages ( 或其别名value )来定义要扫描的特定包。 如果没有定义特定的包,将从声明该注解的类的包开始扫描。

作为了解,不是本文重点。

1.4、探究方向

主要探究图中位于中间部分那条主线,其他只会稍做讲解。

二、@SpringBootConfiguration

我们刚刚已经简单看了一下 @SpringBootConfiguration 啦。

@Configuration
@Indexed
public @interface SpringBootConfiguration {}

它是 springboot 的配置类,标注在某个类上,表示这是一个 springboot的配置类。

我们在这看到 @Configuration ,这个注解我们在 Spring 中就已经看到过了,它的意思就是将一个类标注为 Spring 的配置类,相当于之前 Spring 中的 xml 文件,可以向容器中注入组件。

不是探究重点。

三、@EnableAutoConfiguration

我们来看看这玩意,它的字面意思就是:自动导入配置。

@Inherited
@AutoConfigurationPackage  ////自动导包
@Import(AutoConfigurationImportSelector.class) ////自动配置导入选择
public @interface EnableAutoConfiguration {}

从这里顾名思义就能猜到这里肯定是跟自动配置有关系的。

我们接着来看看这上面的两个注解 @AutoConfigurationPackage 和 @Import(AutoConfigurationImportSelector.class) ,这两个才是我们研究的重点。

3.1、@AutoConfigurationPackage

点进去一看:

@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {}

@Import 为 spring 的注解,导入一个配置文件,在 springboot 中为给容器导入一个组件,而导入的组件由 AutoConfigurationPackages.Registrar.class 执行逻辑来决定的。

往下看:Registrar

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
    }

    @Override
    public Set<Object> determineImports(AnnotationMetadata metadata) {
        return Collections.singleton(new PackageImports(metadata));
    }
}

在这个地方我们可以打个断点,看看  new PackageImports(metadata).getPackageNames().toArray(new String[0]) 它是一个什么值。

我们用 Evaluate 计算 new PackageImports(metadata).getPackageNames().toArray(new String[0]) 出来可以看到就是 com.crush.hello ,当前启动类所在的包。

继续往下看的话就是和 Spring 注册相关了,更深入 xdm 可以继续 debug。

在这里我们可以得到一个小小的结论

@AutoConfigurationPackage 这个注解本身的含义就是将主配置类(@SpringBootApplication 标注的类)所在的包下面所有的组件都扫描到 spring 容器中。

如果将一个 Controller  放到 com.crush.hello  以外就不会被扫描到了,就会报错。

3.2、@Import(AutoConfigurationImportSelector.class)

AutoConfigurationImportSelector  开启自动配置类的导包的选择器(导入哪些组件的选择器)

我们点进 AutoConfigurationImportSelector 类来看看,有哪些重点知识,这个类中存在方法可以帮我们获取所有的配置

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
		ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {

    /**选择需要导入的组件 ,*/
	@Override
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return NO_IMPORTS;
		}
		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	}
    //根据导入的@Configuration类的AnnotationMetadata返回AutoConfigurationImportSelector.AutoConfigurationEntry 。
	protected AutoConfigurationEntry getAutoConfigurationEntry(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);
		//检查
        checkExcludedClasses(configurations, exclusions);
		//删除需要排除的依赖
        configurations.removeAll(exclusions);
        configurations = getConfigurationClassFilter().filter(configurations);
		fireAutoConfigurationImportEvents(configurations, exclusions);
		return new AutoConfigurationEntry(configurations, exclusions);
	}
}

我们看看这个断点,configurations 数组长度为131,并且文件后缀名都为 **AutoConfiguration

这里的意思是将所有需要导入的组件以全类名的方式返回,并添加到容器中,最终会给容器中导入非常多的自动配置类(xxxAutoConfiguration),给容器中导入这个场景需要的所有组件,并配置好这些组件。有了自动配置,就不需要我们自己手写了。

3.2.1、getCandidateConfigurations()

我们还需要思考一下,这些配置都从 getCandidateConfigurations 方法中获取,这个方法可以用来获取所有候选的配置,那么这些候选的配置又是从哪来的呢?

一步一步点进去:

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
   // 这里有个 loadFactoryNames 方法 执行的时候还传了两个参数,一个是BeanClassLoader ,另一个是 getSpringFactoriesLoaderFactoryClass() 我们一起看看
    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;
}

看一下getSpringFactoriesLoaderFactoryClass() 方法,这里传过去的是

protected Class<?> getSpringFactoriesLoaderFactoryClass() {
    return EnableAutoConfiguration.class;
}

这个 EnableAutoConfiguration  是不是特别眼熟,(我们探究的起点 @EnableAutoConfiguration ,有没有感觉自己离答案越来越近啦)

我们再看看 loadFactoryNames() 方法带着它去做了什么处理:

先是将 EnableAutoConfiguration.class 传给了 factoryType ,然后 .getName( ) ,所以factoryTypeName 值为 EnableAutoConfiguration

3.2.2、loadSpringFactories()

接下里又开始调用 loadSpringFactories 方法

这里的 FACTORIES_RESOURCE_LOCATION 在上面有定义:

public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

我们再回到 getCandidateConfigurations 方法处。

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.");

这句断言的意思是:“在 META-INF/spring.factories 中没有找到自动配置类。如果您使用自定义包装,请确保该文件是正确的。“

这个 META-INF/spring.factories 在哪里呢?

里面的内容:

我们日常用到的,基本上都有一个配置类。

比如 webmvc,

我们点进 WebMvcProperties 类中去看一下:

那这里到底是要干什么呢?

这里的意思首先是把这个文件的 urls 拿到之后并把这些 urls 每一个遍历,最终把这些文件整成一个properties 对象,loadProperties方法

然后再从 properties 对象里边获取一些我们需要的值,把这些获取到的值来加载我们最终要返回的这个结果,结果 result 为 map 集合,然后返回到loadFactoryNames方法中。

然后我们再回到  loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList()); 的调用处。

这个 factoryTypeName 值为 EnableAutoConfiguration

因为 loadFactoryNames 方法携带过来的第一个参数为 EnableAutoConfiguration.class,所以 factoryType 值也为 EnableAutoConfiguration.class,那么 factoryTypeName 值为 EnableAutoConfiguration

那么map集合中 getOrDefault 方法为什么意思呢?意思就是当 Map 集合中有这个 key 时,就使用这个 key值,如果没有就使用默认值 defaultValue (第二个参数),所以是判断是否包含 EnableAutoConfiguration

看下图,这不就是嘛?

所以就是把 spring-boot-autoconfigure-2.5.2.jar/META-INF/spring.factories 这个文件下的EnableAutoConfiguration 下面所有的组件,每一个 xxxAutoConfiguration 类都是容器中的一个组件,都加入到容器中。加入到容器中之后的作用就是用它们来做自动配置,这就是Springboot自动配置开始的地方。

只有这些自动配置类进入到容器中以后,接下来这个自动配置类才开始进行启动

那 spring.factories 中存在那么多的配置,每次启动时都是把它们全部加载吗?

是全部加载嘛?不可能的哈,这谁都知道哈,全部加载启动一个项目不知道要多久去了。它是有选择的。

我们随便点开一个类,都有这个 @ConditionalOnXXX 注解

@Conditional 其实是 spring 底层注解,意思就是根据不同的条件,来进行自己不同的条件判断,如果满足指定的条件,那么整个配置类里边的配置才会生效。

所以在加载自动配置类的时候,并不是将 spring.factories 的配置全部加载进来,而是通过这个注解的判断,如果注解中的类都存在,才会进行加载。

这就是SpringBoot的自动配置啦.

四、小结

简单总结起来就是:

启动类中有一个 @SpringBootApplication 注解,包含了 @SpringBootConfiguration、 @EnableAutoConfiguration , @EnableAutoConfiguration 代表开启自动装配,注解会去 spring-boot-autoconfigure 工程下寻找 META-INF/spring.factories 文件,此文件中列举了所有能够自动装配类的清单,然后自动读取里面的自动装配配置类清单。因为有 @ConditionalOn 条件注解,满足一定条件配置才会生效,否则不生效。 如: @ConditionalOnClass(某类.class)  工程中必须包含一些相关的类时,配置才会生效。所以说当我们的依赖中引入了一些对应的类之后,满足了自动装配的条件后,自动装配才会被触发。

到此这篇关于SpringBoot自动配置原理分析的文章就介绍到这了,更多相关SpringBoot自动配置内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • SpringBoot+jpa配置如何根据实体类自动创建表

    目录 jpa配置根据实体类自动创建表 1.配置文件application.properties 2.pom.xml引入包 3.编写实体类 4.运行项目 5.针对项目启动以后数据库并未生成数据库表问题 jpa根据Entry自动生成表 1.加入依赖 2.配置 application.yml 3. 创建Entity jpa配置根据实体类自动创建表 1.配置文件application.properties spring.datasource.url=jdbc:mysql://localhost:3306

  • SpringBoot自动配置实现的详细步骤

    目录 springboot如何实现 一.依赖管理特性 1. 父项目 2. 场景启动器 二.自动配置 1. 自动配置组件 2. 默认的包结构 3. 各种配置拥有默认值 4. 按需加载所有自动配置项 三.小结 springboot如何实现 在之前的 helloworld 示例中,已经初步体会到 springboot 自动导入依赖.完成配置的爽快了. 那么,springboot 是如何实现的呢? 一.依赖管理特性 先看下上一篇内容示例中的pom.xml: <!--导入父工程--> <paren

  • Springboot自动配置与@Configuration配置类详解

    目录 @Configuration 注意点1 注意点2 注意点3 注意点4 springboot自动配置 @Configuration 注意点1 配置类(@Configuration下的这个类)其实相当于一个工厂, 标注 @Bean 注解的方法相当于工厂方法 考虑有如下例子: @Configuration // 注意点1: 配置类其实相当于一个工厂, 标注 @Bean 注解的方法相当于工厂方法 static class MyConfig { @Bean public Bean1 bean1()

  • Springboot2.6.x的启动流程与自动配置详解

    目录 一.Springboot启动流程 1. 第一步对SpringApplication的初始化 2. 第二步SpringApplication具体的启动方案 3.refreshContext:核心启动tomcat流程 二.Springboot自动配置原理 1. @SpringBootApplication 2自动配置流程 3.额外注解学习 总结 一.Springboot启动流程 所有的SpringBoot工程,都有自己的启动类,这个启动类身上有一个固定注解@SpringBootApplicat

  • SpringBoot自动配置原理详解

    目录 阅读收获 一.SpringBoot是什么 二.SpringBoot的特点 三.启动类 3.1 @SpringBootApplication 四.@EnableAutoConfiguration 4.1 @AutoConfigurationPackage 4.2  @Import({AutoConfigurationImportSelector.class}) 五.流程总结图 六.常用的Conditional注解 七.@Import支持导入的三种方式 阅读收获 理解SpringBoot自动配

  • Springboot @Configuration与自动配置详解

    不知道大家第一次搭SpringBoot环境的时候,有没有觉得非常简单.无须各种的配置文件,无须各种繁杂的pom坐标,一个main方法,就能run起来了.与其他框架整合也贼方便,使用EnableXXXXX注解就可以搞起来了! 所以今天来讲讲SpringBoot是如何实现自动配置的~ @SpringBootApplication: Spring Boot应用标注在某个类上说明这个类是SpringBoot的主配置类,SpringBoot需要运行这个类的main方法来启动SpringBoot应用: 先看

  • SpringBoot中@ConfigurationProperties实现配置自动绑定的方法

    目录 代码 构造器绑定 结合@PropertySource 代码 pom.xml: <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=&

  • 聊聊SpringBoot整合Nacos自动刷新配置的问题

    目录 目的 环境 pom 配置文件 代码 日志 测试 目的 Nacos作为SpringBoot服务的注册中心和配置中心. 在NacosServer中修改配置文件,在SpringBoot不重启的情况下,获取到修改的内容. 本例将在配置文件中配置一个 cml.age=100 的配置项,程序中编写一个方法读取配置文件,并通过 Get--->/test/age 接口提供给浏览器访问. 若配置文件中的 age 修改为 200 ,不用重新启动程序,直接访问 /test/age 接口,将获取到最新的值 200

  • 关于springboot加载yml配置文件的no字段自动转义问题

    目录 加载yml配置文件的no字段自动转义 springboot配置文件自动转译的坑 小结一下 加载yml配置文件的no字段自动转义 项目上线了才发现一个字段被转义了,如下图: 本来应该会拿到no字段和数据进行比对的,结果发现比对完的数据这个字段全是null,debug才发现这个字段这么写在加载yml文件之后自动变成了"false",第一次发现这个问题,修改很方便,把yml文件里面这个no,换成'no'就可以不被转义成"false"了,谨以此提醒,小错误导致临时调整

  • SpringBoot自动配置原理分析

    目录 前言 一.启动类 1.1.@SpringBootConfiguration 1.2.@EnableAutoConfiguration 1.3.@ComponentScan 1.4.探究方向 二.@SpringBootConfiguration 三.@EnableAutoConfiguration 3.1.@AutoConfigurationPackage 3.2.@Import(AutoConfigurationImportSelector.class) 3.2.1.getCandidat

  • springboot自动配置原理解析

    前言 小伙伴们都知道,现在市面上最流行的web开发框架就是springboot了,在springboot开始流行之前,我们都用的是strust2或者是springmvc框架来开发web应用,但是这两个框架都有一个特点就是配置非常的繁琐,要写一大堆的配置文件,spring在支持了注解开发之后稍微有些改观但有的时候还是会觉得比较麻烦,这个时候springboot就体现出了它的优势,springboot只需要一个properties或者yml文件就可以简化springmvc中在xml中需要配置的一大堆

  • SpringBoot自动配置原理,你真的懂吗?(简单易懂)

    概述 上面博文(SpringBoot简介与快速搭建)我们简单的介绍了什么是SpringBoot,以及如何使用SpringBoot,但是我们对于SpringBoot的基本原理并没有介绍,这篇博文我们重点介绍SpringBoot是如何实现的自动配置. 依赖管理 在我们的pom文件中最核心的依赖就一个: <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-star

  • SpringBoot 自动配置原理及源码解析

    初始化一个Springboot项目,在主启动类会有这么一个注解:@SpringBootApplication,自动装配的秘密全在主启动类这个注解里面了 点进去一层会发现有三个子注解组成,分别是 @SpringBootConfiguration.@ComponentScan和@EnableAutoConfiguration 接下来分别解释这三个注解在整个自动装配过程中的作用 1.@SpringBootConfiguration 点进去发现它是@Configure,代表当前是一个配置类,意思就是当前

  • 深入浅析SpringBoot自动配置原理

    SpringBoot2.3.1版本源码 一.SpringBoot启动的时候加载主配置类,通过@EnableAutoConfiguration注解开启了自动配置功能 . 二.@EnableAutoConfiguration作用: 1. 点击该注解进入可以发现,它利用AutoConfigurationImportSelector.class 选择器给SpringBoot导入一些组件.导入哪些组件呢?可以点击选择器进入查看selectImports()方法的内容,该方法最终会返回一个configurati

  • springboot自动配置原理以及spring.factories文件的作用详解

    目录 一.springboot 自动配置原理 二.spring.factories文件的作用 spring.factories 的妙用 什么是 SPI 机制? Spring Boot 中的 SPI 机制 Spring Factories 实现原理是什么? Spring Factories 在 Spring Boot 中的应用 一.springboot 自动配置原理 先说说我们自己的应用程序中Bean加入容器的办法: package com.ynunicom.dc.dingdingcontract

  • Java SpringBoot自动配置原理详情

    目录 SpringBoot的底层注解 配置绑定 自动配置原理入门 SpringBoot的底层注解 首先了解一些SpringBoot的底层注解,是如何完成相关的功能的 @Configuration 告诉SpringBoot被标注的类是一个配置类,以前Spring xxx.xml能配置的内容,它都可以做,spring中的Bean组件默认是单实例的 #############################Configuration使用示例###############################

  • Springboot-yaml配置和自动配置原理分析

    目录 版本仲裁中心 自动配置原理 yaml语法 和@PropertySource注解一起使用 和@ImportResource一起使用 配置文件占位符 profile 配置文件的加载顺序 版本仲裁中心 spring dependencies中帮我们依赖了很多常用的jar包, 导入这些jar包不需要版本号 如: <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang

  • 浅谈springboot自动配置原理

    从main函数说起 一切的开始要从SpringbootApplication注解说起. @SpringBootApplication public class MyBootApplication { public static void main(String[] args) { SpringApplication.run(MyBootApplication.class); } } @SpringBootConfiguration @EnableAutoConfiguration @Compon

随机推荐