Spring Boot自动配置:别让Starter变成"坑爹"

avatar
小常在创业IP属地:上海
02026-02-14:22:14:57字数 4040阅读 0

去年在XX项目,我自作主张写了个user-service-starter,结果上线后一跑就崩。原因?没搞懂自动配置的底层逻辑,直接照搬别人的代码,结果依赖没加全,启动时疯狂报错。

别被“自动配置”三个字唬住——它不是魔法,是条件判断+配置文件的组合拳。今天不讲理论,只说实战:怎么写出一个靠谱的Starter,顺便避开我踩过的坑。


一、自动配置的真相:它根本不是“自动”,是“条件触发”

很多人以为Spring Boot的自动配置是“自动装好所有依赖”,其实它更像一个智能管家

“如果用户有Redis依赖,就给我加载Redis配置;如果没Redis,就别瞎折腾。”

关键机制

  1. @EnableAutoConfiguration:启动时扫描spring.factories
  2. spring.factories:列出所有自动配置类
  3. 条件注解:决定配置类是否生效(比如@ConditionalOnClass

我踩过的坑

// 错!没加条件,项目没Redis依赖时直接崩溃
@Configuration
public class RedisAutoConfiguration {
    @Bean
    public RedisTemplate redisTemplate() {
        return new RedisTemplate<>();
    }
}

修复后

@Configuration
// 只有存在Redis类时才生效
@ConditionalOnClass(RedisTemplate.class) 
public class RedisAutoConfiguration {
    @Bean
    public RedisTemplate redisTemplate() {
        return new RedisTemplate<>();
    }
}

教训:条件注解是自动配置的命门,没它就是定时炸弹。


二、自定义Starter实战:从0到1,少走弯路

步骤1:创建Maven模块(别用IDE生成模板)

# 直接手动建目录,避免IDE生成的坑
my-starter/
├── src/main/java/com/example/starter
│   └── RedisAutoConfiguration.java
└── src/main/resources/META-INF
    └── spring.factories

步骤2:写配置类(核心!)

// RedisAutoConfiguration.java
package com.example.starter;

import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;

@Configuration
// 重点!只有存在RedisTemplate类时才加载
@ConditionalOnClass(RedisTemplate.class) 
public class RedisAutoConfiguration {

    // 如果用户没自己配置RedisTemplate,才用我的
    @Bean
    @ConditionalOnMissingBean(RedisTemplate.class) 
    public RedisTemplate redisTemplate() {
        return new RedisTemplate<>();
    }
}

为什么加@ConditionalOnMissingBean

  • 避免和用户自定义的配置冲突(比如用户写了@Bean RedisTemplate
  • 这个坑我栽过两次:用户自己配置了Redis,Starter又初始化一遍,结果Bean冲突

步骤3:配置spring.factories

# META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.starter.RedisAutoConfiguration

关键点

  • 路径必须是META-INF/spring.factories(别写成resources/spring.factories
  • \换行(别用逗号),否则打包后可能失效

三、测试:别等打包后才发现问题

错误示范

写完Starter → 打包 → 拉到主项目里跑 → 报错

正确姿势

// 在Starter模块里写测试类
@SpringBootTest(classes = {RedisAutoConfiguration.class})
class RedisAutoConfigurationTest {
    @Test
    void testRedisTemplate() {
        // 模拟没有Redis依赖的环境
        assertDoesNotThrow(() -> {
            new RedisAutoConfiguration().redisTemplate();
        });
    }
}

为什么重要?

  • 在Starter模块里测试,能提前发现条件注解问题
  • 避免“打包后才发现没Redis依赖就崩溃”

四、避坑指南:我栽过的3个坑

1. spring.factories路径写错

  • 问题:IDE里能看到文件,但打包后找不到
  • 解决:手动检查Maven的target/classes/META-INF/spring.factories
  • 亲测:我有次写了src/main/resources/META-INF/spring.factories,结果打包后路径是META-INF/spring.factories,没被扫描到

2. 条件注解漏了@ConditionalOnMissingBean

  • 问题:用户自己写了RedisTemplate,Starter又初始化一个,Bean冲突
  • 解决:所有@Bean都要加@ConditionalOnMissingBean
  • 例子:
    @Bean
    @ConditionalOnMissingBean(RedisTemplate.class) // 必须加!
    public RedisTemplate redisTemplate() { ... }
    

3. 没处理依赖范围

  • 问题:Starter依赖了spring-boot-starter-data-redis,但用户项目没引入,启动报错
  • 解决:在Starter的pom.xml里把依赖设为provided
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
        <scope>provided</scope> <!-- 关键! -->
    </dependency>
    
    为什么:用户项目需要自己引入Redis依赖,Starter只负责配置

五、真实项目中的Starter:怎么用?

用户项目引入Starter

<!-- 主项目pom.xml -->
<dependency>
    <groupId>com.example</groupId>
    <artifactId>my-starter</artifactId>
    <version>1.0.0</version>
</dependency>

用户代码

// 无需任何配置,直接用
@Autowired
private RedisTemplate redisTemplate;

效果

  • 用户项目必须引入spring-boot-starter-data-redis(否则自动配置不生效)
  • 用户自己没写RedisTemplate,Starter自动注入
  • 用户自己写了RedisTemplate,Starter跳过

最后一句大实话

写Starter不是为了炫技,而是让团队新人30分钟内集成成功。我见过太多团队:

  • 为了“用Starter”硬造一个,结果代码比原生多50%
  • 依赖没处理好,线上事故一出,全员加班

记住

自动配置的精髓是“条件触发,不冲突”。
别写@ConditionalOnClass,直接让项目崩掉;
别写@ConditionalOnMissingBean,让团队重复造轮子。

总资产 0
暂无其他文章

热门文章

暂无热门文章