Skip to content
返回

3分钟替你学会@AutoConfiguration注解

探索下@AutoConfiguration相关的知识点


背景

最近在重构项目,需要把一些公共组件抽离出来做成内部的 Starter,所以重点研究了下@AutoConfiguration注解,整理下资料,想了解又没时间的小伙伴可以跟着我的成果学习下。

在 Spring Boot 2.7 中,“自动配置类写在哪里”需要分两个层面来回答:代码的物理位置和注册的配置位置。

  1. 代码的物理位置(.java 文件放在哪) 自动配置类本身就是一个普通的 Java 类,在自定义 Starter 的模块中,通常遵循以下包命名规范:
com.yourcompany.mystarter
  └── autoconfigure
        └── MyServiceAutoConfiguration.java  <-- 写在这里

(注:官方建议自动配置类不要放在 @ComponentScan 能扫到的默认包路径下,以防止被意外加载,所以通常单独放在 autoconfigure 包中。)

  1. 注册的位置(最关键:Spring Boot 怎么知道去哪找它?) 在 Spring Boot 2.7 中,自动配置类的全类名必须注册在 spring.factories 文件中。 你需要在你的 Starter 项目的 resources/META-INF/ 目录下创建一个名为 spring.factories 的文件,并按照如下格式写入:
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.yourcompany.mystarter.autoconfigure.MyServiceAutoConfiguration,\
  com.yourcompany.mystarter.autoconfigure.AnotherAutoConfiguration

原理:Spring Boot 启动时,会通过 SpringFactoriesLoader 去所有 jar 包的 META-INF/spring.factories 文件中寻找 EnableAutoConfiguration 这个 Key 对应的类,然后将它们实例化并加载到 Spring 容器中。

💡 补充:Spring Boot 2.7 的特殊“过渡”身份 你需要知道,Spring Boot 2.7 是一个过渡版本。在这个版本里,有两种写法都可以生效:

写法一:传统方式(2.7及之前的标准) 就是上面提到的,写在 META-INF/spring.factories 文件里。类上面可以只用 @Configuration,或者用 2.7 刚刚引入的新注解 @AutoConfiguration。

写法二:新方式(为 3.0 做准备) 在 2.7 中,Spring Boot 已经开始支持 3.0 的新规范。你可以不写 spring.factories,而是把全类名写在新的文件里: resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

com.yourcompany.mystarter.autoconfigure.MyServiceAutoConfiguration

双重扫描

@AutoConfiguration的注解本质还是一个@Component,什么过滤逻辑都没有。

@ComponentScan 的底层逻辑是非常简单粗暴的——只要类路径下符合过滤规则(比如带有 @Component 及其派生注解),它就会一股脑全部抓取进来。

在 Spring Boot 3.2 之前,@ComponentScan 根本不认识 @AutoConfiguration 是个特殊的注解。 在它眼里,这就是个普通的配置类。

这样 Spring Boot 加载 Bean 就会有两条完全独立的轨道去加载@AutoConfiguration:

轨道 A(主路):主启动类上的 @SpringBootApplication 会触发 @ComponentScan,扫描当前包及子包下的所有组件。 轨道 B(辅路):@SpringBootApplication 里的 @EnableAutoConfiguration 会触发 AutoConfigurationImportSelector,它只去读 imports 文件(或旧版的 spring.factories),然后把里面的类实例化。

所以官方文档里那句“自动配置类必须放在 @ComponentScan 扫不到的包路径下”,就是防止双重扫描的防线。

在开发 Starter 时,我们的包名通常是 com.yourcompany.mystarter.autoconfigure,而业务应用的包名是 com.yourcompany.app。因为包路径不同,业务应用的 @ComponentScan 天然扫不到 Starter 里的类,所以相安无事。

这并不是框架层面做到了隔离,而是利用了 Java 包名空间的物理隔离。一旦手滑把包名写重合了,就会立马踩坑。

Spring Boot 3.2:迟到三年的真正闭环

这个状态,一直持续到了 Spring Boot 3.2(配合 Spring Framework 6.1)。

在这个版本里,Spring 官方终于修改了底层扫描器 ClassPathScanningCandidateComponentProvider 的逻辑。 在 3.2 的源码中,Spring 真正增加了一个针对 @AutoConfiguration 的排除过滤器:

// Spring Framework 6.1 源码片段
protected void registerDefaultFilters() {
    // ... 其他逻辑
    // 新增:排除带有 @AutoConfiguration 注解的类
    addExcludeFilter(new AnnotationTypeFilter(AutoConfiguration.class));
}

这意味着,从 3.2 开始,“隔离组件扫描”才真正从概念变成了物理现实。 现在,即使你把 @AutoConfiguration 类写在主启动类的同一个包下,@ComponentScan 在底层扫到它时,也会直接无视跳过。它只会老老实实地等待 imports 文件通过轨道 B 来加载它。双重加载的隐患被彻底抹除了。

总结

理清一下 Spring Boot 装配机制的过程:

  1. @AutoConfiguration 的核心价值不在于它怎么被扫描,而在于它规范了自动配置类的语义,并提供了原生的排序属性(before/after)。
  2. 在 Spring Boot 2.7 到 3.1 期间,它并没有实现真正的扫描隔离,千万不要把它写在业务代码的包下,否则必定触发双重加载的连环炸弹。
  3. Spring Boot 3.2 补齐了最后一块拼图,在底层扫描器中直接排除了它,才让“隔离”二字真正名副其实。

技术演进往往就是这样,一个设计理念提出来(比如 2.7 提出的概念),可能要经过几个大版本的迭代,底层实现才能彻底追上最初的愿景。如果你目前的版本还在 3.2 之前,写 Starter 的时候,包路径可千万要看住了。


📌 关注公众号《秋雨》,专注技术人成长与职场真实记录。

something


Share this post on:

Previous Post
Spring Boot 4 模块化架构扫盲笔记
Next Post
35岁程序员核心竞争力