🚀今天,我们来详细的聊一聊SpringBoot自动配置原理,学了这么久,你学废了吗?

简介: 🚀今天,我们来详细的聊一聊SpringBoot自动配置原理,学了这么久,你学废了吗?

QQ截图20220525194427.png


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

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


前言:


阅读完本文:


  1. 你能知道 SpringBoot 启动时的自动配置的原理知识


  1. 你能知道 SpringBoot 启动时的自动配置的流程


  1. 以及对于 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  的 元素并行的支持。 可以 basePackageClassesbasePackages ( 或其别名value )来定义要扫描的特定包。 如果没有定义特定的包,将从声明该注解的类的包开始扫描。


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


1.4、探究方向


微信截图_20220525194632.png


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


二、@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 {}


@Importspring 的注解,导入一个配置文件,在 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]) 它是一个什么值。


微信截图_20220525194740.png


我们用 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


微信截图_20220525194823.png


这里的意思是将所有需要导入的组件以全类名的方式返回,并添加到容器中,最终会给容器中导入非常多的自动配置类(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() 方法带着它去做了什么处理:


QQ截图20220525194911.png


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


3.2.2、loadSpringFactories()


接下里又开始调用 loadSpringFactories 方法


微信截图_20220525194939.png


这里的 FACTORIES_RESOURCE_LOCATION  在上面有定义:


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


我们再回到 getCandidateConfigurations 方法处。


QQ截图20220525195006.png


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 在哪里呢?


QQ截图20220525195042.png


里面的内容:


QQ截图20220525195113.png


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


比如 webmvc,


QQ截图20220525195134.png


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


微信截图_20220525195200.png


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


微信截图_20220525195220.png


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


微信截图_20220525195240.png


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


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


QQ截图20220525195304.png


这个 factoryTypeName 值为 EnableAutoConfiguration

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


QQ截图20220525195328.png


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

看下图,这不就是嘛?


微信截图_20220525195352.png


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


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


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

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


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


微信截图_20220525195418.png


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


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

这就是SpringBoot的自动配置啦.


四、小结


简单总结起来就是:


启动类中有一个 @SpringBootApplication 注解,包含了 @SpringBootConfiguration@EnableAutoConfiguration@EnableAutoConfiguration 代表开启自动装配,注解会去 spring-boot-autoconfigure 工程下寻找  META-INF/spring.factories  文件,此文件中列举了所有能够自动装配类的清单,然后自动读取里面的自动装配配置类清单。因为有 @ConditionalOn 条件注解,满足一定条件配置才会生效,否则不生效。 如:  


@ConditionalOnClass(某类.class) 工程中必须包含一些相关的类时,配置才会生效。所以说当我们的依赖中引入了一些对应的类之后,满足了自动装配的条件后,自动装配才会被触发。


微信截图_20220525195458.png


五、自言自语


纸上得来终觉浅,绝知此事要躬行。


如果可以,可以自己 debug 一遍,画一画流程图。🛌 (躺平)


你好,我是博主宁在春主页

希望本篇文章能让你感到有所收获!!!

我们:待别日相见时,都已有所成

如有疑惑,大家可以留言评论。

如有不足之处,请大家指出来,非常感谢 👨‍💻。


目录
相关文章
|
29天前
|
人工智能 Java 开发者
【Spring】原理解析:Spring Boot 自动配置
Spring Boot通过“约定优于配置”的设计理念,自动检测项目依赖并根据这些依赖自动装配相应的Bean,从而解放开发者从繁琐的配置工作中解脱出来,专注于业务逻辑实现。
|
3月前
|
Java Spring 容器
SpringBoot自动配置的原理是什么?
Spring Boot自动配置核心在于@EnableAutoConfiguration注解,它通过@Import导入配置选择器,加载META-INF/spring.factories中定义的自动配置类。这些类根据@Conditional系列注解判断是否生效。但Spring Boot 3.0后已弃用spring.factories,改用新格式的.imports文件进行配置。
759 0
|
3月前
|
Java Spring
Spring Boot配置的优先级?
在Spring Boot项目中,配置可通过配置文件和外部配置实现。支持的配置文件包括application.properties、application.yml和application.yaml,优先级依次降低。外部配置常用方式有Java系统属性(如-Dserver.port=9001)和命令行参数(如--server.port=10010),其中命令行参数优先级高于系统属性。整体优先级顺序为:命令行参数 &gt; Java系统属性 &gt; application.properties &gt; application.yml &gt; application.yaml。
614 0
|
22天前
|
缓存 Java 应用服务中间件
Spring Boot配置优化:Tomcat+数据库+缓存+日志,全场景教程
本文详解Spring Boot十大核心配置优化技巧,涵盖Tomcat连接池、数据库连接池、Jackson时区、日志管理、缓存策略、异步线程池等关键配置,结合代码示例与通俗解释,助你轻松掌握高并发场景下的性能调优方法,适用于实际项目落地。
228 4
|
29天前
|
传感器 Java 数据库
探索Spring Boot的@Conditional注解的上下文配置
Spring Boot 的 `@Conditional` 注解可根据不同条件动态控制 Bean 的加载,提升应用的灵活性与可配置性。本文深入解析其用法与优势,并结合实例展示如何通过自定义条件类实现环境适配的智能配置。
探索Spring Boot的@Conditional注解的上下文配置
|
2月前
|
安全 算法 Java
在Spring Boot中应用Jasypt以加密配置信息。
通过以上步骤,可以在Spring Boot应用中有效地利用Jasypt对配置信息进行加密,这样即使配置文件被泄露,其中的敏感信息也不会直接暴露给攻击者。这是一种在不牺牲操作复杂度的情况下提升应用安全性的简便方法。
728 10
|
7月前
|
缓存 Java API
微服务——SpringBoot使用归纳——Spring Boot集成 Swagger2 展现在线接口文档——Swagger2 的配置
本文介绍了在Spring Boot中配置Swagger2的方法。通过创建一个配置类,添加`@Configuration`和`@EnableSwagger2`注解,使用Docket对象定义API文档的详细信息,包括标题、描述、版本和包路径等。配置完成后,访问`localhost:8080/swagger-ui.html`即可查看接口文档。文中还提示了可能因浏览器缓存导致的问题及解决方法。
765 0
微服务——SpringBoot使用归纳——Spring Boot集成 Swagger2 展现在线接口文档——Swagger2 的配置
|
3月前
|
前端开发 Java 数据库连接
SpringBoot参数校验底层原理和实操。深度历险、深度解析(图解+秒懂+史上最全)
SpringBoot参数校验底层原理和实操。深度历险、深度解析(图解+秒懂+史上最全)
SpringBoot参数校验底层原理和实操。深度历险、深度解析(图解+秒懂+史上最全)
|
7月前
|
Java 关系型数据库 数据库
微服务——SpringBoot使用归纳——Spring Boot事务配置管理——Spring Boot 事务配置
本文介绍了 Spring Boot 中的事务配置与使用方法。首先需要导入 MySQL 依赖,Spring Boot 会自动注入 `DataSourceTransactionManager`,无需额外配置即可通过 `@Transactional` 注解实现事务管理。接着通过创建一个用户插入功能的示例,展示了如何在 Service 层手动抛出异常以测试事务回滚机制。测试结果表明,数据库中未新增记录,证明事务已成功回滚。此过程简单高效,适合日常开发需求。
958 0
|
7月前
|
Java 测试技术 微服务
微服务——SpringBoot使用归纳——Spring Boot中的项目属性配置——少量配置信息的情形
本课主要讲解Spring Boot项目中的属性配置方法。在实际开发中,测试与生产环境的配置往往不同,因此不应将配置信息硬编码在代码中,而应使用配置文件管理,如`application.yml`。例如,在微服务架构下,可通过配置文件设置调用其他服务的地址(如订单服务端口8002),并利用`@Value`注解在代码中读取这些配置值。这种方式使项目更灵活,便于后续修改和维护。
104 0