Spring Boot Starter 的手写示例

几种常见的组件导入方式

在 Spring Boot 项目中,如果你在公共模块 @Configuration 配置类,需要在其他模块中使用,通常有以下几种常见的方式,我们进行逐一介绍。


组件扫描(最常用)

1
2
3
4
5
6
7
@SpringBootApplication
@ComponentScan(basePackages = {"com.xxx.common", "com.xxx.admin"})
public class AdminApplication {
public static void main(String[] args) {
SpringApplication.run(AdminApplication.class, args);
}
}


通过 @Import 导入

在当前模块项目的配置类或启动类上直接导入公共模块的配置类:

1
2
3
4
5
6
7
@SpringBootApplication
@Import({CommonConfig1.class, CommonConfig2.class})
public class AdminApplication {
public static void main(String[] args) {
SpringApplication.run(AdminApplication.class, args);
}
}


通过 @ImportResource 导入

在当前模块项目的配置类或启动类上直接导入公共模块的配置类:

1
2
3
4
5
6
7
@SpringBootApplication
@ImportResource(value = {"classpath:config1.xml", "classpath:spring/config2.xml"}) // xml 注入业务 Bean
public class AdminApplication {
public static void main(String[] args) {
SpringApplication.run(AdminApplication.class, args);
}
}


利用自动配置

如果公共模块中定义了自动配置类,可以在 META-INF/spring.factories 中注册:

1
2
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.xxx.common.config.CommonConfig

或者使用 Spring Boot 2.7+ 推荐的 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件:

1
com.xxx.common.config.CommonConfig


手写一个自动配置 starter 的示例

以下这个 Starter 会实现一个简单的短信发送服务,包含自动配置、属性绑定和条件加载。

项目结构

1
2
3
4
5
6
7
8
9
10
my-sms-spring-boot-starter/
├── pom.xml
└── src/main/java/com/owlias/sms/
├── SmsProperties.java # 配置属性类
├── SmsService.java # 服务接口
├── SmsServiceImpl.java # 服务实现
├── SmsAutoConfiguration.java # 自动配置类
└── resources/META-INF/
└── spring/
└── org.springframework.boot.autoconfigure.AutoConfiguration.imports


项目依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<?xml version="1.0" encoding="UTF-8"?>
<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>

<groupId>com.owlias</groupId>
<artifactId>my-sms-spring-boot-starter</artifactId>
<version>1.0.0</version>

<properties>
<java.version>17</java.version>
<spring-boot.version>3.5.13</spring-boot.version>
</properties>

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

<!-- 配置属性处理器(可选,用于 IDE 提示) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>${spring-boot.version}</version>
<optional>true</optional>
</dependency>

<!-- Lombok(可选) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<optional>true</optional>
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.17</version>
</dependency>
</dependencies>
</project>


配置属性类

SmsProperties

1
2
3
4
5
6
7
8
9
10
@Data
@ConfigurationProperties(prefix = "sms")
public class SmsProperties {
private boolean enabled = true;
private String provider = "aliyun";
private String apiKey;
private String apiSecret;
private String signName;
private int connectTimeout = 5000;
}


服务接口和实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public interface SmsService {
boolean send(String phoneNumber, String message);
boolean sendVerificationCode(String phoneNumber, String code);
}


public class SmsServiceImpl implements SmsService {
private static final Logger log = LoggerFactory.getLogger(SmsServiceImpl.class);
private final SmsProperties properties;

public SmsServiceImpl(SmsProperties properties) {
this.properties = properties;
}

@Override
public boolean send(String phoneNumber, String message) {
// 模拟发送短信
log.info("=== 短信发送 ===");
log.info("提供商: {}", properties.getProvider());
log.info("签名: {}", properties.getSignName());
log.info("手机号: {}", phoneNumber);
log.info("内容: {}", message);
log.info("================");

// 实际项目中这里调用第三方 SDK
// return aliyunSdk.send(phoneNumber, message);
return true;
}

@Override
public boolean sendVerificationCode(String phoneNumber, String code) {
String message = String.format("您的验证码是:%s,5分钟内有效。", code);
return send(phoneNumber, message);
}
}


自动配置类

SmsAutoConfiguration

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@AutoConfiguration
@EnableConfigurationProperties(SmsProperties.class)
@ConditionalOnClass(SmsService.class)
@ConditionalOnProperty(prefix = "sms", name = "enabled", havingValue = "true", matchIfMissing = true)
public class SmsAutoConfiguration {
private static final Logger log = LoggerFactory.getLogger(SmsAutoConfiguration.class);

private final SmsProperties properties;
public SmsAutoConfiguration(SmsProperties properties) {
this.properties = properties;
log.info("短信服务自动配置已初始化");
}

@Bean
@ConditionalOnMissingBean(SmsService.class)
public SmsService smsService() {
log.info("创建短信服务 Bean,提供商: {}", properties.getProvider());
return new SmsServiceImpl(properties);
}
}


Spring Boot 3.x 注册文件

创建文件:src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

1
com.owlias.sms.SmsAutoConfiguration


在使用方调用

导入上述 sms starter 依赖后,先在 application.yml 声明:

1
2
3
4
5
6
7
sms:
enabled: true
provider: aliyun
api-key: your-api-key
api-secret: your-api-secret
sign-name: 腾讯云短信
connect-timeout: 3000

在业务代码中使用:

1
2
3
4
5
6
7
8
9
10
11
@Service
public class UserService {

@Autowired
private SmsService smsService;

public void register(String phone) {
// 发送验证码
smsService.sendVerificationCode(phone, "123456");
}
}

禁用 Starter:

1
2
sms:
enabled: false # 关闭整个短信模块

或者在启动类排除:

1
@SpringBootApplication(exclude = SmsAutoConfiguration.class)