自定义 Spring Boot Starter 开发指南

自定义 Spring Boot Starter 面试要点

1. 什么是 Spring Boot Starter?

Spring Boot Starter 是一组方便的依赖描述符,你可以在项目中包含这些描述符,
从而获得所需的所有依赖项。它们简化了依赖管理和配置,使得开发者能够快速启动和运行
Spring Boot 应用程序。

2. 为什么需要自定义 Starter?

  1. 代码复用:将常用的功能封装成可重用的组件
  2. 简化配置:减少重复配置工作
  3. 统一管理:集中管理特定功能的实现
  4. 团队协作:提供标准化的解决方案

3. 自定义 Starter 的核心组件

3.1 配置属性类 (@ConfigurationProperties)

@ConfigurationProperties(prefix = "my.starter")
public class MyProperties {
    private String prefix = "Hello";
    private String suffix = "!";
    private boolean enabled = true;
    private int repeat = 1;
    // getter/setter...
}

3.2 业务服务类

public class MyService {
    private final MyProperties properties;
    
    public MyService(MyProperties properties) {
        this.properties = properties;
    }
    
    // 实现业务逻辑...
}

3.3 自动配置类

@Configuration
@ConditionalOnClass(MyService.class)
@ConditionalOnProperty(prefix = "my.starter", name = "enabled", 
                       havingValue = "true", matchIfMissing = true)
@EnableConfigurationProperties(MyProperties.class)
public class MyAutoConfiguration {
    
    @Bean
    @ConditionalOnMissingBean(MyService.class)
    public MyService myService(MyProperties properties) {
        return new MyService(properties);
    }
}

3.4 配置文件 (META-INF/spring.factories)

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.std.mystarter.MyAutoConfiguration

4. 关键注解说明

  • @ConfigurationProperties:用于绑定配置文件中的属性到 Java 对象
  • @ConditionalOnClass:当类路径下存在指定类时才进行配置
  • @ConditionalOnProperty:根据配置属性的值决定是否进行配置
  • @ConditionalOnMissingBean:当容器中不存在指定类型的 Bean 时才创建
  • @EnableConfigurationProperties:启用配置属性绑定功能

5. 实现步骤总结

  1. 创建业务类:实现核心功能
  2. 创建配置属性类:定义可配置的参数
  3. 创建自动配置类:定义 Bean 并使用条件注解控制加载时机
  4. 注册自动配置:在 spring.factories 文件中注册配置类
  5. 添加配置元数据:提供 IDE 提示支持

6. 条件注解的使用场景

  • @ConditionalOnClass:依赖的类存在时才配置
  • @ConditionalOnMissingBean:避免重复创建 Bean
  • @ConditionalOnProperty:根据配置开关决定是否启用
  • @ConditionalOnResource:资源存在时才配置
  • @ConditionalOnWebApplication:Web 环境下才配置

7. 最佳实践

  1. 命名规范:使用 xxx-spring-boot-starter 的命名方式
  2. 依赖传递:确保 Starter 包含所有必要依赖
  3. 配置验证:对配置参数进行有效性校验
  4. 文档完善:提供清晰的使用说明
  5. 版本管理:合理管理依赖版本

8. 注意事项

  • 避免循环依赖
  • 合理使用条件注解
  • 提供合理的默认值
  • 考虑线程安全问题
  • 做好异常处理

9. 实际应用场景

  • 数据库连接池配置
  • 缓存集成
  • 消息队列集成
  • 监控和健康检查
  • 日志框架集成
  • 第三方 API 集成

1. 项目概述

本项目演示了如何开发一个自定义的 Spring Boot Starter - service-zstarter,以及如何在另一个模块 my-bootstarter-test 中使用它。

2. 项目结构

spring-cloud-alibaba-demo/
├── service-zstarter/           # 自定义 Starter 模块
│   ├── src/main/java/com/std/mystarter/
│   │   ├── MyAutoConfiguration.java    # 自动配置类
│   │   ├── MyProperties.java           # 配置属性类
│   │   ├── MyService.java              # 业务服务类
│   │   └── MyCondition.java            # 条件判断类
│   ├── src/main/resources/META-INF/
│   │   └── additional-spring-configuration-metadata.json  # 配置元数据
│   └── pom.xml
└── my-bootstarter-test/        # 测试模块
    ├── src/main/java/com/std/DemoApplication.java
    ├── src/main/resources/application.yml
    └── pom.xml

3. 开发步骤详解

3.1 创建自定义 Starter (service-zstarter)

3.1.1 定义业务服务类 (MyService.java)
package com.std.mystarter;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MyService {
    private static final Logger logger = LoggerFactory.getLogger(MyService.class);
    private final MyProperties properties;

    public MyService(MyProperties properties) {
        this.properties = properties;
        logger.info("MyService initialized with properties: {}", properties);
    }

    public String sayHello(String name) {
        if (!properties.isEnabled()) {
            return "Service is disabled";
        }

        StringBuilder result = new StringBuilder();
        for (int i = 0; i < properties.getRepeat(); i++) {
            result.append(properties.getPrefix())
                    .append(" ")
                    .append(name)
                    .append(properties.getSuffix());
            if (i < properties.getRepeat() - 1) {
                result.append(" ");
            }
        }
        return result.toString();
    }

    public void logConfig() {
        logger.info("MyService Configuration:");
        logger.info("  Prefix: {}", properties.getPrefix());
        logger.info("  Suffix: {}", properties.getSuffix());
        logger.info("  Enabled: {}", properties.isEnabled());
        logger.info("  Repeat: {}", properties.getRepeat());
    }
}
3.1.2 定义配置属性类 (MyProperties.java)
package com.std.mystarter;

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

@ConfigurationProperties(prefix = "my.starter")
public class MyProperties {
    // 默认配置值
    private String prefix = "Hello";
    private String suffix = "!";
    private boolean enabled = true;
    private int repeat = 1;

    // Getter 和 Setter 方法
    public String getPrefix() { return prefix; }
    public void setPrefix(String prefix) { this.prefix = prefix; }
    
    public String getSuffix() { return suffix; }
    public void setSuffix(String suffix) { this.suffix = suffix; }
    
    public boolean isEnabled() { return enabled; }
    public void setEnabled(boolean enabled) { this.enabled = enabled; }
    
    public int getRepeat() { return repeat; }
    public void setRepeat(int repeat) { this.repeat = repeat; }
}
3.1.3 创建自动配置类 (MyAutoConfiguration.java)
package com.std.mystarter;

import org.springframework.context.annotation.Configuration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
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;

@Configuration  // Spring Boot 2.x 使用 @Configuration
@ConditionalOnClass(MyService.class)  // 类路径存在 MyService 时才生效
@ConditionalOnProperty(prefix = "my.starter", name = "enabled",
        havingValue = "true", matchIfMissing = true)
@EnableConfigurationProperties(MyProperties.class)  // 使配置属性生效
public class MyAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean(MyService.class)
    public MyService myService(MyProperties properties) {
        return new MyService(properties);
    }

    @Bean
    @ConditionalOnMissingBean(name = "myServiceHelper")
    public MyServiceHelper myServiceHelper(MyService myService) {
        return new MyServiceHelper(myService);
    }
}
3.1.4 添加配置元数据文件 (additional-spring-configuration-metadata.json)
{
  "properties": [
    {
      "name": "my.starter.prefix",
      "type": "java.lang.String",
      "description": "消息前缀",
      "defaultValue": "Hello"
    },
    {
      "name": "my.starter.suffix",
      "type": "java.lang.String",
      "description": "消息后缀",
      "defaultValue": "!"
    },
    {
      "name": "my.starter.enabled",
      "type": "java.lang.Boolean",
      "description": "是否启用服务",
      "defaultValue": true
    },
    {
      "name": "my.starter.repeat",
      "type": "java.lang.Integer",
      "description": "消息重复次数",
      "defaultValue": 1
    }
  ]
}
3.1.5 配置自动装配 (spring.factories)

src/main/resources/META-INF/spring.factories 文件中添加:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.std.mystarter.MyAutoConfiguration
3.1.6 配置 POM 文件 (pom.xml)
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.std</groupId>
        <artifactId>spring-cloud-alibaba-demo</artifactId>
        <version>1.0.0</version>
    </parent>
    <artifactId>service-zstarter</artifactId>
    <packaging>jar</packaging>

    <name>service-zstarter</name>
    <url>http://maven.apache.org</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <!-- Spring Boot 自动配置核心依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
        </dependency>

        <!-- 配置属性处理 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

        <!-- 条件注解支持 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <!-- 可选:如果需要日志 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>
</project>

3.2 测试自定义 Starter (my-bootstarter-test)

3.2.1 创建测试应用 (DemoApplication.java)
package com.std;

import com.std.mystarter.MyService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication implements CommandLineRunner {

    @Autowired
    private MyService myService;

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @Override
    public void run(String... args) {
        System.out.println("测试自定义 Starter:");
        System.out.println(myService.sayHello("Spring Boot"));
        myService.logConfig();
    }
}
3.2.2 配置自定义参数 (application.yml)
my:
  starter:
    prefix: "Hi"
    suffix: "!!!"
    enabled: true
    repeat: 3

# 启用调试模式查看自动配置报告
debug: true
3.2.3 配置测试模块依赖 (pom.xml)
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.std</groupId>
        <artifactId>spring-cloud-alibaba-demo</artifactId>
        <version>1.0.0</version>
    </parent>

    <artifactId>my-bootstarter-test</artifactId>
    <packaging>jar</packaging>

    <name>my-bootstarter-test</name>
    <url>http://maven.apache.org</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.std</groupId>
            <artifactId>service-zstarter</artifactId>
            <version>1.0.0</version>
        </dependency>
    </dependencies>
</project>

4. 关键概念说明

4.1 自动配置原理

  • 使用 [@ConditionalOnClass](file:///E:/javaWork/std/spring-cloud-alibaba-demo/service-user/src/main/java/com/std/config/RedisConfig.java#L8-L8) 注解确保类路径中有必要的类时才进行配置
  • 使用 [@ConditionalOnProperty](file:///E:/javaWork/std/spring-cloud-alibaba-demo/service-user/src/main/java/com/std/config/RedisConfig.java#L9-L9) 注解根据配置属性决定是否启用配置
  • 使用 [@ConditionalOnMissingBean](file:///E:/javaWork/std/spring-cloud-alibaba-demo/service-user/src/main/java/com/std/config/RedisConfig.java#L10-L10) 注解确保只有在没有相同类型的 Bean 时才创建新 Bean

4.2 配置属性绑定

  • 使用 [@ConfigurationProperties](file:///E:/javaWork/std/spring-cloud-alibaba-demo/common/src/main/java/com/std/config/RedisConfig.java#L10-L10) 注解将配置文件中的属性绑定到 Java 对象
  • 配置前缀使用 my.starter,对应的配置项如 my.starter.prefix

4.3 条件化配置

  • Spring Boot 提供了多种条件注解,可以根据不同条件激活配置
  • 常用的条件注解包括 [@ConditionalOnClass](file:///E:/javaWork/std/spring-cloud-alibaba-demo/service-user/src/main/java/com/std/config/RedisConfig.java#L8-L8)、[@ConditionalOnMissingBean](file:///E:/javaWork/std/spring-cloud-alibaba-demo/service-user/src/main/java/com/std/config/RedisConfig.java#L10-L10)、[@ConditionalOnProperty](file:///E:/javaWork/std/spring-cloud-alibaba-demo/service-user/src/main/java/com/std/config/RedisConfig.java#L9-L9) 等

5. 构建和使用

要构建和使用此自定义 Starter,请执行以下步骤:

  1. 构建整个项目:mvn clean install
  2. 运行测试应用:cd my-bootstarter-test && mvn spring-boot:run
  3. 应用将输出自定义配置的结果

6. 最佳实践

  1. 命名规范:Starter 名称通常以 -spring-boot-starter 结尾
  2. 依赖管理:确保 Starter 包含所有必需的依赖项
  3. 配置元数据:提供配置元数据以支持 IDE 提示
  4. 条件配置:合理使用条件注解避免冲突
  5. 文档说明:提供清晰的使用说明和示例
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值