自动配置原理与自定义自动配置(深入浅出 + 实战 starter 示例)

举报
喵手 发表于 2026/01/15 17:31:51 2026/01/15
【摘要】 开篇语哈喽,各位小伙伴们,你们好呀,我是喵手。运营社区:C站/掘金/腾讯云/阿里云/华为云/51CTO;欢迎大家常来逛逛  今天我要给大家分享一些自己日常学习到的一些知识点,并以文字的形式跟大家一起交流,互相学习,一个人虽可以走的更快,但一群人可以走的更远。  我是一名后端开发爱好者,工作日常接触到最多的就是Java语言啦,所以我都尽量抽业余时间把自己所学到所会的,通过文章的形式进行输出,...

开篇语

哈喽,各位小伙伴们,你们好呀,我是喵手。运营社区:C站/掘金/腾讯云/阿里云/华为云/51CTO;欢迎大家常来逛逛

  今天我要给大家分享一些自己日常学习到的一些知识点,并以文字的形式跟大家一起交流,互相学习,一个人虽可以走的更快,但一群人可以走的更远。

  我是一名后端开发爱好者,工作日常接触到最多的就是Java语言啦,所以我都尽量抽业余时间把自己所学到所会的,通过文章的形式进行输出,希望以这种方式帮助到更多的初学者或者想入门的小伙伴们,同时也能对自己的技术进行沉淀,加以复盘,查缺补漏。

小伙伴们在批阅的过程中,如果觉得文章不错,欢迎点赞、收藏、关注哦。三连即是对作者我写作道路上最好的鼓励与支持!

1) 自动配置的基本原理(启动时发生了什么)

  • Spring Boot 在启动时会从类路径下查找 “自动配置候选类” 列表(历史上是 META-INF/spring.factories,新版本推荐/使用 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports),把这些配置类逐个加载并判断条件(如 @ConditionalOnClass@ConditionalOnMissingBean 等),满足条件的配置类会被应用到 Spring 上下文,进而注册 Bean。

  • 关键点:所谓“自动配置”本质就是一系列带条件的 @Configuration(或标注为 @AutoConfiguration)类,这些配置类不是靠组件扫描发现的,而是通过 自动配置注册表(上面那个 META-INF 文件)告诉 Spring Boot:“启动时请考虑这些配置”。只有当配置类上所有条件成立时,才会真正注入 Bean。

2) 常用条件注解解析(带“我会怎么用”的建议)

下面列出最常见也最实用的几种条件注解,并解释常见误区。

@ConditionalOnClass

  • 含义:只有当指定类在 classpath 上可见时,才匹配通过(也可用类名字符串避免类加载问题)。这通常用于“如果应用引入了某个可选依赖,则启用相关自动配置”。
  • 使用建议:当你的自动配置依赖某个可选库(例如 spring-webspring-redis、第三方 SDK),用 @ConditionalOnClass(SomeClass.class) 把配置包裹起来,避免在缺少该依赖时出现 ClassNotFoundException
  • 常见坑:把 @ConditionalOnClass 写在 @Bean 方法上且用返回值类型做匹配,可能会因为类加载顺序导致判断问题;通常把条件放在配置类级别更稳妥。

@ConditionalOnMissingBean

  • 含义:当 IoC 容器中不存在某类/名称/注解匹配的 Bean 时,条件成立(也就是——只有没有用户定义的同类型 Bean 时,才注入默认 Bean)。
  • 使用建议:这是实现“可覆盖默认实现”的首选注解。你的 starter 提供默认 Bean,但应用可以通过显式声明同接口/同类型 Bean 来覆盖它。
  • 常见坑:声明 @ConditionalOnMissingBean 时要注意类型匹配(接口 vs 实现类 vs 返回类型),有时因为泛型或候选类型不一致导致条件失配,从而出现“默认 Bean 没注入 / 无法覆盖”的问题。

其它有用条件(列举并简短说明)

  • @ConditionalOnProperty:基于配置属性启/停(例如 my.starter.enabled=true)。非常适合给 starter 增加开关。
  • @ConditionalOnBean:要求容器已有某 Bean(用于“只有在存在某体系时才启用”)。
  • @ConditionalOnMissingClass@ConditionalOnExpression 等:做更细化条件判断。
    (关于条件注解的实现、语义与边界,官方有完整说明,可参考 docs)。

3) 自动配置加载顺序与优先级

自动配置类之间的顺序对最终结果可能影响很大(尤其当两个 auto-config 都能创建同类型 Bean 时)。Spring Boot 提供了注解来控制顺序:

  • @AutoConfigureBefore / @AutoConfigureAfter:声明某个自动配置类应当在另一个之前或之后应用(通常写类名或类全名字符串)。适用于你需要在别的自动配置之前/之后生效的场景(例如 web 相关配置)。

  • @AutoConfigureOrder:直接指定顺序值(不常用,更多是配合 @AutoConfiguration 时使用)。

原则:尽量写“局部且稳妥”的依赖顺序(before/after)而不是全局靠 order 值乱设,因为值大小难以全局协调。

4) 自定义自动配置模块(构建 starter)——实战(完整代码示例)

下面我们一步步做一个最小可用的 starter:greeting-starter。它的目的很简单:提供一个 GreetingService 的默认实现,并暴露 greeting.message 配置项。应用若引入该 starter,会自动得到默认 GreetingService;如果应用自己声明一个 GreetingService,则默认实现会被覆盖。

说明:示例用 Java + Maven。示例兼容 Spring Boot 2.x / 3.x 的基本做法;注意 META-INF 注册方式在新旧版本上有差别(见注册节)。官方 auto-config 指南是权威参考。

4.1 项目骨架(maven 坐标 & 结构)

greeting-starter/
  src/main/java/com/example/greeting/     # 代码
    GreetingService.java
    DefaultGreetingService.java
    GreetingProperties.java
    GreetingAutoConfiguration.java
  src/main/resources/META-INF/
    spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
  pom.xml

pom.xml(核心片段):

<project ...>
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.example</groupId>
  <artifactId>greeting-starter</artifactId>
  <version>0.1.0</version>
  <packaging>jar</packaging>

  <dependencies>
    <!-- 只依赖 spring-boot-autoconfigure & spring-boot-configuration-processor(optional) -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-autoconfigure</artifactId>
      <version>${spring.boot.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-configuration-processor</artifactId>
      <optional>true</optional>
    </dependency>
  </dependencies>
</project>

备注:spring-boot-configuration-processor 用于在 IDE 中自动生成 @ConfigurationProperties 的元数据,方便开发者补全和验证。


4.2 定义接口与默认实现

GreetingService.java

package com.example.greeting;

public interface GreetingService {
    String greet(String name);
}

DefaultGreetingService.java

package com.example.greeting;

public class DefaultGreetingService implements GreetingService {
    private final String message;

    public DefaultGreetingService(String message) {
        this.message = message;
    }

    @Override
    public String greet(String name) {
        return String.format("%s, %s!", message, name);
    }
}

4.3 配置属性类(@ConfigurationProperties

GreetingProperties.java

package com.example.greeting;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = "greeting")
public class GreetingProperties {
    /**
     * 是否启用 starter 提供的功能(默认 true)
     */
    private boolean enabled = true;

    /**
     * 默认问候语
     */
    private String message = "Hello";

    // getters & setters
    public boolean isEnabled() { return enabled; }
    public void setEnabled(boolean enabled) { this.enabled = enabled; }
    public String getMessage() { return message; }
    public void setMessage(String message) { this.message = message; }
}

4.4 自动配置类(关键:@AutoConfiguration + 条件注解)

GreetingAutoConfiguration.java

package com.example.greeting;

import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;

@AutoConfiguration
@EnableConfigurationProperties(GreetingProperties.class)
@ConditionalOnProperty(prefix = "greeting", name = "enabled", havingValue = "true", matchIfMissing = true)
public class GreetingAutoConfiguration {

    private final GreetingProperties properties;

    public GreetingAutoConfiguration(GreetingProperties properties) {
        this.properties = properties;
    }

    @Bean
    @ConditionalOnMissingBean
    public GreetingService greetingService() {
        return new DefaultGreetingService(properties.getMessage());
    }
}

解释与要点:

  • @AutoConfiguration:Spring Boot 推荐用于自动配置类的注解(等价/改进于以前的 @Configuration + 桩)。自动配置类通过 META-INF/...AutoConfiguration.imports 被发现并考虑。
  • @EnableConfigurationProperties(GreetingProperties.class):确保 GreetingProperties 被绑定并注入到配置类构造函数中。
  • @ConditionalOnProperty(prefix="greeting", name="enabled", havingValue="true", matchIfMissing=true):提供一个开关,方便使用方通过 greeting.enabled=false 来禁用 starter。
  • @ConditionalOnMissingBean:若应用已提供 GreetingService 的 Bean,则不会注入默认实现,从而实现覆盖能力。

4.5 在 META-INF 注册(新旧注册文件)

新方式(Spring Boot 推荐,Spring Boot 3.x+)

创建文件:

src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

内容:

com.example.greeting.GreetingAutoConfiguration

旧方式(历史兼容):

META-INF/spring.factories
# spring.factories 里的一行示例(旧)
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.greeting.GreetingAutoConfiguration

小结:较新 Spring Boot 版本倾向于使用 ...AutoConfiguration.imports 文件来声明自动配置候选类,社区/官方也在逐步迁移这个机制(参考为何从 spring.factories 迁移的讨论)。如果你的 starter 要兼容多版本 Boot,可同时提供两者(注意维护)。

4.6 使用方(应用如何引入并覆盖)

应用 pom.xml 添加依赖:

<dependency>
  <groupId>com.example</groupId>
  <artifactId>greeting-starter</artifactId>
  <version>0.1.0</version>
</dependency>

application.yml(启用自定义配置):

greeting:
  enabled: true
  message: "Hi from my starter"

覆盖默认实现(在应用中自定义 Bean)

@Bean
public GreetingService myGreetingService() {
    return name -> "Yo " + name; // 将覆盖 starter 的 DefaultGreetingService
}

注入使用

@RestController
public class HelloController {
    private final GreetingService greetingService;

    public HelloController(GreetingService greetingService) {
        this.greetingService = greetingService;
    }

    @GetMapping("/hello")
    public String hello(@RequestParam(defaultValue = "World") String name) {
        return greetingService.greet(name);
    }
}

4.7 测试自动配置(简单集成测试示例)

测试 1:当依赖存在且未覆盖时,bean 应存在

@SpringBootTest(properties = {"greeting.enabled=true"})
public class GreetingAutoConfigTest {

    @Autowired
    ApplicationContext context;

    @Test
    void greetingServicePresent() {
        assertTrue(context.containsBean("greetingService"));
        GreetingService s = context.getBean(GreetingService.class);
        assertEquals("Hello, Alice!", s.greet("Alice")); // 如果 properties.message 默认 Hello
    }
}

测试 2:当应用自定义 Bean 时,自动配置的 Bean 不生效

@SpringBootTest(classes = {GreetingAutoConfigTest.CustomConfig.class})
public class GreetingAutoConfigOverrideTest {

    @Configuration
    static class CustomConfig {
        @Bean
        public GreetingService greetingService() {
            return name -> "Custom " + name;
        }
    }

    @Autowired
    GreetingService greetingService;

    @Test
    void userBeanOverrides() {
        assertEquals("Custom Bob", greetingService.greet("Bob"));
    }
}

5) 常见陷阱与调试技巧(为什么我的自动配置“没生效”?)

  1. 没把自动配置类写入 META-INF 注册文件:最常见的错误。没有注册的自动配置永远不会被考虑(除非被组件扫描捕获,但那不是自动配置机制的正确用法)。

  2. 条件注解写错了或放错位置:把 @ConditionalOnClass 放在 @Bean 方法上而不是配置类上,或对类型指定得不够精确,可能导致判断失败或类加载异常。优先在类级别写条件更稳定。

  3. 忘记 @EnableConfigurationProperties / properties 未绑定:结果是 GreetingProperties 无法注入或值为默认值,导致行为与预期不符。确保在自动配置类上启用 properties。

  4. 与其他 auto-config 的顺序冲突:如果你的 auto-config 需要在 web auto-config 之后才能正确工作,记得用 @AutoConfigureAfter(WebMvcAutoConfiguration.class) 或对应的 before/after 指定顺序。

  5. 组件扫描误用:不要通过组件扫描来实现自动配置(例如把自动配置类放在应用可能会扫描到的包下),正确做法是通过 META-INF/...AutoConfiguration.imports 注册。否则会出现“在某些环境下生效、在另一些环境下不生效”的不稳定行为。

  6. 版本兼容与注册文件差异:不同 Spring Boot 版本在自动配置注册机制上有差别(spring.factoriesAutoConfiguration.imports),要注意兼容策略或明确你的目标 Boot 版本。

6) 小结与建议(何时写 starter,如何发布/维护)

  • 什么时候写 starter? 当你有一组复用组件(SDK、公共库、业务中台功能)需要在多个应用中一致配置与装配时,写 starter 能显著降低接入成本与配置重复。

  • 如何维护好 starter?

    • 明确语义化配置项(prefix + 详尽注释),并提供 application.yml 示例。
    • 提供配置开关(enabled),使使用者能快速禁用。
    • 测试覆盖:提供自动化测试,覆盖“未引入依赖”“用户覆盖 Bean”“属性启/停”等场景。
    • 发布时注意 Spring Boot 兼容性(如果可能,给出多个版本分支或说明兼容的 Boot 版本)。
  • 调试技巧:启动应用时用 --debug 或查看 ConditionEvaluationReport,Spring Boot 会输出条件匹配信息,能帮助你定位哪个条件没通过(为什么某个 auto-config 被跳过)。官方文档有相关说明。

参考资料(权威来源,值得收藏)

  • Spring Boot 官方:Creating your own auto-configuration / Developing auto-configuration(自动配置开发指南)。
  • @ConditionalOnClass API 说明(官方 javadoc)。
  • @ConditionalOnMissingBean API 说明(官方 javadoc)。
  • 关于 spring.factories → AutoConfiguration.imports 的讨论(StackOverflow / issue),用于理解注册机制演进。
  • Auto-configuration 顺序讨论(StackOverflow / 官方 - before/after 注解)。

结尾(诚恳建议与“别犯的错”清单)

如果你现在只是想验证思路:先做一个本地的 greeting-starter,把 jar 安装到本地仓库(mvn install),再在一个 demo 项目里引入依赖、测试启用/禁用/覆盖。

如果你要发布到公司内部或公共仓库:记得写好 README(包含配置项、示例、兼容性说明)、加上 spring-boot-configuration-processor 的注解处理器以生成元数据、并尽量提供 spring.factories + AutoConfiguration.imports 双注册以兼容不同 Boot 版本(如果你需要支持老版本)。

… …

文末

好啦,以上就是我这期的全部内容,如果有任何疑问,欢迎下方留言哦,咱们下期见。

… …

学习不分先后,知识不分多少;事无巨细,当以虚心求教;三人行,必有我师焉!!!

wished for you successed !!!


⭐️若喜欢我,就请关注我叭。

⭐️若对您有用,就请点赞叭。
⭐️若有疑问,就请评论留言告诉我叭。


版权声明:本文由作者原创,转载请注明出处,谢谢支持!

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。