news 2026/3/28 10:14:57

SpringBoot自动配置原理

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SpringBoot自动配置原理

问题引入

SpringBoot最核心的一个东西就是自动配置,自动配置的原理是什么?

我们知道Spring最开始是通过xml文件配置,来告诉Spring要创建哪些类、哪些Bean。

后来进化为可以通过注解来配置,这样就不用去写xml文件了,只需要告诉Spring去扫描哪些包就可以了。

那么问题来了,如果是一个三方服务框架包,我怎么知道要去扫描哪些包呢?

很多时候我们还是只能通过@Bean的方式去创建,一些复杂的服务可能需要创建很多Bean,我们怎么知道要创建哪些Bean呢?这些Bean的关联关系又是怎样的呢?

用起来太麻烦了,很多时候,我们并不想知道实现的具体细节,我们只希望拿来就能用,怎么办呢?

于是:SpringBoot来了,它可以自动完成配置,创建并注入需要的Bean,不需要我们自己去创建了,若有的创建Bean的工作都由提供服务的服务者去创建,他们自己实现的,当然更清楚怎么用,怎么创建系列Bean。

那么,问题又来了,SpringBoot是怎么做到自动配置的呢?

答案是:类Java SPI机制。

Java SPI机制

一个好的语言、框架设计时候一定要考虑好扩展,做好抽象层。

例如Java的数据库驱动就是一个好的例子,我们使用的都是java.sql包下的抽象层接口,根本不用关心是哪种数据库,实现细节是什么。

一个不好的例子,就是Java的日志功能,所以抽象做得更好的slf4j-api基本就成了共用接口标准。

当然,技术在不断发展,很多时候,我们再设计的时候并不能考虑到那么多,例如响应式编程,那也是发展了很多年,才有projectreactor

Java的数据库驱动为什么能让我们不用关心具体实现呢,答案就是:SPI(Service Provider Interface)。

Java SPI核心类是:java.util.ServiceLoader

它会去扫描classpath下的META-INF/services目录下的文件,文件名是抽象层的全限定名,例如java.sql.Driver,文件内容是服务提供的具体实现类。

如:com.mysql.cj.jdbc.Driver

Java SPI Druid Driver

@EnableAutoConfiguration

Java SPI的核心类是ServiceLoader,SpringBoot自动配置的核心类是@EnableAutoConfiguration。

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

EnableAutoConfiguration它通过@Import(AutoConfigurationImportSelector.class)自动装配了AutoConfigurationImportSelector类。

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

AutoConfigurationImportSelector实现了DeferredImportSelector接口,DeferredImportSelector又实现了ImportSelector。

ImportSelector#selectImports就可以告诉Spring这些类需要被导入。

那AutoConfigurationImportSelector会去哪里找需要自动装配的类呢?

顺着接口selectImports找呗,有兴趣可以自己去跟源码,这里简单总结一下:

  1. ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader())
  2. 找的是AutoConfiguration类
  3. META-INF/spring/要找的类的全限定名称.imports文件
  4. 所以:META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
  5. 上面的文件配置的类,每行一个全部导入

还有一个ImportAutoConfigurationImportSelector,是处理ImportAutoConfiguration。

org.springframework.boot.autoconfigure.AutoConfiguration.imports

除了直接导入的,还有通过org.springframework.core.io.support.SpringFactoriesLoader#loadFactoryNames导入,找的是META-INF/spring.factories文件。

spring.factories这个文件中的内容类似properties,是key-value形式,key是接口全限定名,value是具体实现类全限定名,多个逗号分隔。

spring.factories

对于自动导入来说,具体的接口是org.springframework.boot.autoconfigure.EnableAutoConfiguration。

例如:

@Import({DruidSpringAopConfiguration.class, DruidStatViewServletConfiguration.class, DruidWebStatFilterConfiguration.class, DruidFilterConfiguration.class}) public class DruidDataSourceAutoConfigure {}

对过对于EnableAutoConfiguration,还是推荐使用新的规范,仍在META-INF\spring\org.springframework.boot.autoconfigure.AutoConfiguration.imports文件中,这样不用配置key,也不用考虑多行的问题,直接每行一个就行。

实现自己的EnableMyAutoConfiguration

知道了原理,我们可以动手实现一个我们自己的EnableMyAutoConfiguration。

首先是Enable注解:

import org.springframework.context.annotation.Import; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Import(MyAutoConfigurationImportSelector.class) public @interface EnableMyAutoConfiguration { }

然后是,我们需要处理什么注解呢?我们自定义的MyAutoConfiguration

import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface MyAutoConfiguration { }

接下来,我们需要一个DeferredImportSelector来加载配置的MyAutoConfiguration类。

import org.springframework.boot.context.annotation.ImportCandidates; import org.springframework.context.annotation.DeferredImportSelector; import org.springframework.core.type.AnnotationMetadata; import java.util.List; public class MyAutoConfigurationImportSelector implements DeferredImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { List<String> configurations = ImportCandidates.load(MyAutoConfiguration.class, this.getClass().getClassLoader()) .getCandidates(); return configurations.toArray(new String[0]); } }

使用了@MyAutoConfiguration的配置类:

import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Import; @MyAutoConfiguration @Import(BzClass.class) public class ConfigurationA { @Bean public String name(){ return "ConfigurationA"; } }

配置类中导入了具体的业务类:

public class BzClass { }

然后我们把配置类vip.oschool.custom.ConfigurationA,配置到META-INF\spring\vip.oschool.custom.MyAutoConfiguration.imports文件中。

然后,来一个启动类:

import org.springframework.context.annotation.ConfigurationClassPostProcessor; import org.springframework.context.support.GenericApplicationContext; @EnableMyAutoConfiguration public class Main { public static void main(String[] args) { GenericApplicationContext context = new GenericApplicationContext(); context.registerBean("main", Main.class); // ConfigurationClassPostProcessor处理DeferredImportSelector context.registerBean(ConfigurationClassPostProcessor.class); context.refresh(); for (String beanName : context.getBeanDefinitionNames()) { System.out.println(beanName); } } }

手动实现自动加载

结果,我们可以看到,所有的配置类都被加载了。

starter

知道了自动配置原理,再来看starter就想吃饭喝水一样简单了。

通常我们自己的state人的artifactId是xxxx-spring-boot-starter,spring自己的stater是spring-boot-starter-xxxx

druid-spring-boot-starter就是把配置类添加到了spring.factories

druid自动配置实现

DruidDataSourceAutoConfigure创建了DruidDataSourceWrapper,又通过@Import导入DruidSpringAopConfiguration等实际业务类。

所以,我们什么都不用管,只需要把druid-spring-boot-starter添加入依赖就可以用了,因为DruidSpringAopConfiguration都帮我们创建好了。

当然,关联的依赖也在druid-spring-boot-starter的pom.xml中导入了。

还有一些更符合Spring模式的,例如,会单独有一个autoconfigure项目来管理自动配置。

自动配置的类配置在META-INF\spring\org.springframework.boot.autoconfigure.AutoConfiguration.imports文件。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/27 8:58:53

2025年最值得收藏的GitHub大模型开源项目合集,小白也能轻松上手

文章精选了5个GitHub热门开源项目&#xff1a;Awesome知识库(370kStar)、self-llm大模型入门指南、mindsDB AI查询引擎、Qlib AI量化投资平台和R&D-Agent智能体框架。这些项目覆盖大模型学习、数据查询、量化投资和AI研发等领域&#xff0c;帮助程序员和AI爱好者快速掌握技…

作者头像 李华
网站建设 2026/3/27 14:21:03

解决leetcode第3791题.给定范围内平衡整数的数目

3791.给定范围内平衡整数的数目难度&#xff1a;困难问题描述&#xff1a;给你两个整数low和high。如果一个整数同时满足以下两个条件&#xff0c;则称其为平衡整数&#xff1a;它至少包含两位数字。偶数位置上的数字之和等于奇数位置上的数字之和&#xff08;最左边的数字位置…

作者头像 李华
网站建设 2026/3/27 17:28:40

GitHub Wiki编写PyTorch项目文档

GitHub Wiki 编写 PyTorch 项目文档 在深度学习项目开发中&#xff0c;最让人头疼的往往不是模型结构设计或调参技巧&#xff0c;而是“为什么你的代码在我机器上跑不起来&#xff1f;”——这个看似简单的问题背后&#xff0c;隐藏着环境依赖混乱、CUDA 版本冲突、Python 包版…

作者头像 李华
网站建设 2026/3/27 7:17:51

分布式训练容错机制:PyTorch Eager与FSDP对比

分布式训练容错机制&#xff1a;PyTorch Eager与FSDP对比 在当今大模型时代&#xff0c;一次训练动辄持续数天甚至数周&#xff0c;GPU集群每小时的计算成本可能高达数百美元。如果因为某个节点突然宕机导致整个训练任务中断&#xff0c;不仅意味着巨大的时间损失&#xff0c;更…

作者头像 李华
网站建设 2026/3/27 15:00:37

基于PLC的智能交通灯控制系统设计

基于PLC的智能交通灯控制系统设计 第一章 引言 在城市道路交通管理中&#xff0c;交通灯是规范车流、人流秩序的核心设施&#xff0c;其控制合理性直接影响通行效率与交通安全。传统交通灯多采用固定时序控制&#xff0c;无法根据实时车流量、行人流量动态调整信号时长&#xf…

作者头像 李华