当前位置: 首页 > news >正文

SpringBoot--自动配置原理详解

为什么要学习自动配置原理?

原因:在实际开发中,我们经常会定义一些公共的组件,提供各个团队来使用,为了使用方便,我们经常会将公共的组件自定义成starter,如果想自定义starter,必须来了解自动配置原理。

遵循约定大于配置的原则,在springboot程序启动后,起步依赖中的一些bean对象会自动注入到IOC容器中。

这时就需要去springboot中的源码中解析了。

源码分析:

程序中引入spring-boot-starter-web起步依赖后,启动就会自动往IOC容器中注入DispatcherServlet。

代码展示:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>
 @SpringBootApplicationpublic class SpringbootAutoConfigApplication {​public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(SpringbootAutoConfigApplication.class, args);System.out.println(context.getBean(DispatcherServlet.class));}}

分析源码

启动类@SpringBootApplication是一个组合注解,其中组合了三个注解。

image-20250514212232454

第一个注解@springBootConfiguration

作用是声明启动类也是一个配置类:源码如下

 @Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Configuration@Indexedpublic @interface SpringBootConfiguration {@AliasFor(annotation = Configuration.class)boolean proxyBeanMethods() default true;}
第二个注解(重点):@EnableAutoConfiguration

这是自动配置的核心注解。源码如下:

image-20250514212801491

之前在学习Bean注册时,@Import的第二种用法:@EnableXxxx注解,用于封装@Import注解(导入ImportSelector 接口实现类)。

再看源码。这不就是我们在学习Bean注册使用的方法吗,在进入AutoConfigurationImportSelector类中一探究竟!

public class AutoConfigurationImportSelector implements DeferredImportSelector
public interface DeferredImportSelector extends ImportSelector

这说明AutoConfigurationImportSelector就是ImportSelect接口的实现类。

既然是ImportSelect接口的实现类,那一定重写了selectImports方法。那我们之前在学习注册Bean时,曾说过,selectImports方法会被springboot自动调用从而得到返回的全类名的字符串数组,然后将对应的Bean对象注入到IOC容器中。

但是在源码中springboot并不会调用selectImports方法.

因为此时AutoConfigurationImportSelector实现的是ImportSelector的子类 DeferredImportSelector。而springboot针对于DeferredImportSelector接口的实现类有另外一组处理流程,在这个流程中并不会调用这个selectImports方法。

但是我们还是可以通过解析selectImports方法学习自动配置的原理。

继续回忆:当时在学习注册Bean时,selectImports方法是通过返回一个全类名的数组,从而将Bean对象注入IOC容器中,这些全类名也是从配置文件中读取出来再输入数组中。

再看源码,springboot这里也是需要去读取配置文件中的全类名再返回,而在源码中是调用getAutoConfigurationEntry(annotationMetadata)新建autoConfigurationEntry对象调用getConfigurations()方法,可以缩减成

return StringUtils.toStringArray(getAutoConfigurationEntry(annotationMetadata).getConfigurations());

因此重点就是getAutoConfigurationEntry()方法,这里面应该可以得知配置文件的位置。

image-20250514214550651

继续跟进,去看getAutoConfigurationEntry()方法的源码,可以看到返回了一个AutoConfigurationEntry对象,里面有两个参数,其中configurations应该和我们的配置文件有关,

继续跟进,发现通过调用getCandidateConfigurations方法生成字符串数组。

image-20250514215517843

继续跟进,去查看getCandidateConfigurations方法源码:

至此,我们终于找到了配置文件的所在地,同时也绕不开一个类:autoConfigurationAnnotation类,这个类决定了配置文件,所以需要继续跟进。

image-20250514222459371

同时我们在解析springboot中的starter的起步依赖

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId>
</dependency>

在跟进解析

image-20250514223156768

我们在去导入的依赖库中寻找

image-20250514223710667

进入到这些imports文件中,里面果然是一堆类的全类名。而且都是自动配置类。

image-20250514224251069

我们也可以去看看这些自动配置类如何书写的,我们可以去找DispatcherServletAutoConfiguration

找到之后,发现DispatcherServletAutoConfiguration类上声明了一个@AutoConfiguration注解,(自动配置)

image-20250514224955426

我们去看下@AutoConfiguration注解的源码

image-20250514225614006

同时我们在DispatcherServletAutoConfiguration类上也发现了一个老熟人,在我们学习注册条件时,学到的Conditional注解的衍生注解@ConditionalOnClass: 当前环境存在指定的类时,该注解声明下的Bean才会被注册。

在这里的意思就是@ConditionalOnClass(DispatcherServlet.class):如果说运行环境中有DispatcherServlet类,这是DispatcherServletAutoConfiguration类(DispatcherServlet自动配置类)就生效!就可以自动注入一个DispatcherServlet Bean对象。

继续分析源码:

我们在DispatcherServletAutoConfiguration中还发现了一个内部配置类DispatcherServletConfiguration。

而且这个内部配置类里面的方法就是我们学习Bean注册时所用的注入方法,用@Bean注解声明,返回DispatcherServlet对象。

而且这里面最核心的地方在于,

将DispatcherServletAutoConfiguration写进指定的配置文件中

这时,springboot就可以自动的到配置文件中读取,将自动配置类的对象注入到IOC容器中。

又由于自动配置类的内部还有一个配置类,且内部配置类中还有用@Bean声明的方法,

因此,springboot还是会继续解析,直至解析完毕,将所有的Bean对象注入到IOC容器中。

image-20250514231219136

总结:自动配置的核心就是"META-INF/spring/"+ this.autoConfigurationAnnotation.getName() + ".imports配置文件

在springboot2.7版本之前,自动配置使用的这个配置文件是spring.factories,springboot会从这个配置文件中读取自动配置类的全类名。

在springboot2.7到3.0之间,springboot同时兼容了.imports文件以及spring.factories,但是在3.0以后就只支持.imports文件了。

至此。自动配置的原理已经明了。

再回到最开始,我们在学习Bean注册时的代码是自动配置吗?

明显没有,因为我们还需要去手动的提供配置类,还需要去写Imports注解。

如果要达到自动配置的效果,配置类不应该是自己提供。

应该是谁提供Bean对象,谁就应该提供这些配置类,因此配置类应该由jar包提供。

接下来我们还要jar包提供一个自动配置类,并且在自动配置类上声明两个注解:@AutoConfiguration(声明自动配置类),还有@Import()(导入配置类。

最后我们需要提供一个.imports配置文件,将自动配置类的全类名放在.imports配置文件中。这样我们就达到了自动配置的效果.

案例展示

自定义maven项目,结合Bean注册以及注册条件中的知识创建自动配置项目并打包成jar包common-pojo-2.0

首先新建Maven项目,新建src目录及resources目录,在如下建立包

image-20250517152559691

代码如下:

pom.xml:

     <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId><version>3.4.5</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.38</version></dependency></dependencies>

pojo包下

Country

 @Data@AllArgsConstructor@NoArgsConstructorpublic class Country {private String name;private String code;}

Province

 @Data@AllArgsConstructor@NoArgsConstructorpublic class Province {private String  name;private String direction;}

config包下

commonConfig

 public class CommonConfig {@ConditionalOnProperty(prefix = "country",value = {"name","code"})@Bean//注入country对象public Country country(@Value("${country.name}") String name, @Value("${country.code}") String code){return new Country(name, code);}//    @Bean("aa")//如果方法内部需要使用IOC容器中的bean,则需要传入参数,springboot会自动传入//如果方法内部没有使用IOC容器中的bean,则不需要传入参数@Beanpublic Province province(){return new Province();}}

commonAutoConfig

@AutoConfiguration@Import({CommonConfig.class})public class CommonAutoConfig {public CommonAutoConfig() {}}

META-INF包下 org.springframework.boot.autoconfigure.AutoConfiguration.imports

 com.lyc.config.CommonAutoConfig

代码如上所示:

最终使用maven工具打包,从target包下拿到jar包

image-20250517153325328

将jar包与该项目放于同一文件夹下。

在命令行中输入以下指令

 mvn install:install-file  -Dfile="E:\桌面\测试\common-pojo-2.0-1.0-SNAPSHOT.jar" -DgroupId=com.lyc -DartifactId=common-pojo-2.0 -Dversion=1.0-SNAPSHOT -Dpackaging=jar

最终在pom文件中输入依赖

 <dependency><groupId>com.lyc</groupId><artifactId>common-pojo-2.0</artifactId><version>1.0-SNAPSHOT</version></dependency>

最终得到:

image-20250517153648192

进行测试,看Province Bean对象是否注入IOC容器中

在配置文件中给属性赋值

 country:name: 中国code: CN
 public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(SpringbootAutoConfigApplication.class, args);System.out.println(context.getBean(DispatcherServlet.class));System.out.println(context.getBean("province"));System.out.println(context.getBean("country"));}

结果:

image-20250517153955267

测试成功

面试高频:说一说springBoot自动配置原理?

首先在主启动类上添加了@SpringBootApplication注解,这是一个组合注解,其中包含了@EnableAutoConfiguration注解。这个注解就是开启自动配置的关键注解。

在@EnableAutoConfiguration注解中,又组合了@Import注解,导入了AutoConfigurationImportSelector类,这类实现了selectImports方法,这个方法经过层层调用,最终会读取META-INF目录下的后缀名为imports的文件。

在springboot2.7版本以前,读取的是spring.factories文件,在2.7到3.0之间兼容,3.0版本之后只允许读取imports文件。

这个imports文件中包含了许多自动配置类的全类名,springboot底层会自动读取imports文件中的全类名。

解析注册条件,也就是@Conditional及其衍生注解,把满足注册条件的Bean对象自动注入到IOC容器中。

希望对大家有所帮助!

相关文章:

  • 从专家编码到神经网络学习:DTM 的符号操作新范式
  • FauxGen:一款由 CodeBuddy 主动构建的假数据生成器
  • 【算法】分支限界法和贪心、动态规划、回溯、分治法的区别是
  • 强化学习笔记(一)基本概念
  • 多模态学习(三)—— ROPE位置编码:从理论到实践
  • 文字溢出省略号显示
  • React 中,闭包陷阱
  • 8天Python从入门到精通【itheima】-14~16
  • 10 分钟打造一款超级马里奥小游戏,重拾20 年前的乐趣
  • Baklib知识中台构建企业智能服务新引擎
  • Linux安全篇 --firewalld
  • 使用教程:8x16模拟开关阵列可级联XY脚双向导通自动化接线
  • 江协科技GPIO输入输出hal库实现
  • 每日Prompt:生成自拍照
  • YOLOv8 目标检测算法深度解析
  • flutter长列表 ListView、GridView、SingleChildScrollView、CustomScrollView区别
  • 汽车Wafer连接器:工业设备神经网络的隐形革命者
  • 豆粕ETF投资逻辑整理归纳-20250511
  • c++函数调用运算符及类型转换运算符重载
  • 线程同步学习
  • 看展 | 黄永玉新作展,感受赤子般的生命力
  • 中国新闻发言人论坛在京举行,郭嘉昆:让中国声音抢占第一落点
  • 霍步刚任辽宁沈阳市委书记
  • 李成钢:近期个别经济体实施所谓“对等关税”,严重违反世贸组织规则
  • 吉利汽车一季度净利润大增264%,称整合极氪后实现整体效益超5%
  • 《大风杀》导演张琪:为了不算计观众,拍了部不讨好的警匪片