🎯 先说说为啥要自己造 Starter
在企业级微服务架构中,通用组件的配置管理往往面临挑战。每个服务都要重复配置 Redis、MQ、监控等,容易出现版本不一致或配置错误。将通用组件打包成 Starter 后,部署时间大幅缩短,配置错误率降低,新人上手速度显著提升。
本文不仅教你怎么写 Starter,更要教你怎么写出生产级的 Starter。这中间的坑,我都替你踩过了。
✨ 摘要
Spring Boot Starter 是企业级微服务架构的基石。本文从实战出发,完整解析 Starter 的开发全流程:从 Maven 配置、自动装配原理、条件注解使用,到配置元数据、健康检查、监控集成。通过多个真实企业级 Starter 案例(分布式锁、ID 生成器、审计日志),提供可复用的代码模板和最佳实践。最后分享 Starter 治理、版本兼容、性能优化等高级话题。
1. 别急着写代码,先想清楚这几个问题
1.1 什么样的组件适合做成 Starter?
不是所有组件都适合做成 Starter。我总结了三个判断标准:
✅ 标准一:跨项目复用性强
比如:Redis 客户端、消息队列、分布式锁、ID 生成器。这些每个服务都要用。
✅ 标准二:配置复杂但模式固定
比如:数据库连接池配置、线程池配置、SSL 证书配置。手动配容易出错。
✅ 标准三:需要统一管理和升级
比如:监控上报、链路追踪、安全组件。需要全公司统一。
反面教材:有人把业务层的 DTO 做成 Starter,这就属于走火入魔了。
1.2 Starter 的命名规范:别瞎起名
命名看起来是小问题,但很重要。Spring 官方有明确的命名规范:
<artifactId>spring-boot-starter-data-redis</artifactId>
<artifactId>redis-spring-boot-starter</artifactId>
<artifactId>my-redis-starter</artifactId>
<artifactId>redis-starter-spring-boot</artifactId>
企业内部的命名建议:
<artifactId>example-spring-boot-starter-distributed-lock</artifactId>
<artifactId>payment-spring-boot-starter-audit</artifactId>
<artifactId>common-spring-boot-starter-id-generator</artifactId>
2. Starter 的核心架构:不只是@Configuration
2.1 一个完整 Starter 的组成
很多人以为 Starter 就是几个 Java 类,太天真了!一个生产级的 Starter 至少包含:
- 核心功能实现
- 自动配置类
- 属性绑定类
- 依赖管理
- 文档与测试
2.2 自动装配的原理再深入一点
我知道你肯定看过@EnableAutoConfiguration 的原理,但我今天要说点不一样的。
关键点:Spring Boot 2.7 之后,自动配置的注册方式变了:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.MyAutoConfiguration
com.example.MyAutoConfiguration
@AutoConfiguration
@EnableConfigurationProperties(MyProperties.class)
@ConditionalOnClass(SomeClass.class)
public class MyAutoConfiguration {
}
代码清单 1:新旧自动配置注册方式对比
为什么这么改?因为 @AutoConfiguration 支持更多特性:
- 自动排序(通过
@AutoConfigureBefore、@AutoConfigureAfter)
- 更好的 IDE 支持
- 更清晰的元数据
3. 实战:手把手写一个分布式锁 Starter
3.1 需求分析:我们要解决什么问题?
在做技术方案前,先明确需求。我们需要的分布式锁要:
- 支持多种实现:Redis、Zookeeper、数据库
- 可配置:超时时间、重试策略
- 监控:锁获取成功率、平均耗时
- 易用:注解式、编程式都要支持
3.2 项目结构设计
先看 Maven 项目结构:
distributed-lock-spring-boot-starter/
├── pom.xml
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── example/
│ │ │ └── lock/
│ │ │ ├── LockProperties.java # 配置属性
│ │ │ ├── DistributedLock.java # 核心接口
│ │ │ ├── RedisDistributedLock.java # Redis 实现
│ │ │ ├── ZkDistributedLock.java # ZK 实现
│ │ │ ├── LockAutoConfiguration.java # 自动配置
│ │ │ ├── LockAspect.java # AOP 切面
│ │ │ └── annotation/
│ │ │ └── Lockable.java # 锁注解
│ │ └── resources/
│ │ ├── META-INF/
│ │ │ └── spring/
│ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports
│ │ └── application-lock.yml # 默认配置
│ └── test/ # 测试代码
└── README.md
3.3 核心代码实现
第一步:定义配置属性
@ConfigurationProperties(prefix = "example.lock")
@Validated
public class LockProperties {
@NotEmpty(message = "锁类型不能为空")
private String type = "redis";
@Min(value = 1, message = "超时时间必须大于 0")
private long defaultTimeout = 30000;
@Min(value = 0, message = "等待时间不能小于 0")
private long defaultWaitTime = 10000;
@Min(value = 0, message = "重试次数不能小于 0")
private int retryTimes = 3;
private RedisConfig redis = new RedisConfig();
private ZkConfig zookeeper = new ZkConfig();
@Data
public {
;
String password;
;
;
;
}
{
;
;
;
;
}
}
代码清单 2:分布式锁配置属性类
第二步:定义核心接口
public interface DistributedLock {
boolean tryLock(String lockKey, long timeout, long waitTime);
default boolean tryLock(String lockKey) {
return tryLock(lockKey, 30000, 10000);
}
void unlock(String lockKey);
boolean renewLock(String lockKey, long expireTime);
LockStats getStats();
@Data
class LockStats {
private long totalAcquireAttempts;
private long successfulAcquisitions;
private long failedAcquisitions;
private long averageAcquireTime;
private Map<String, Long> keyStats;
}
}
代码清单 3:分布式锁核心接口
第三步:实现 Redis 分布式锁
@Slf4j
public class RedisDistributedLock implements DistributedLock {
private final RedissonClient redissonClient;
private final LockProperties properties;
private final ConcurrentHashMap<String, RLock> lockCache = new ConcurrentHashMap<>();
private final LockStats stats = new LockStats();
private final AtomicLong totalAcquireAttempts = new AtomicLong();
private final AtomicLong successfulAcquisitions = new AtomicLong();
public RedisDistributedLock(LockProperties properties) {
this.properties = properties;
Config config = new Config();
RedisConfig redisConfig = properties.getRedis();
config.useSingleServer()
.setAddress(redisConfig.getAddress())
.setPassword(redisConfig.getPassword())
.setDatabase(redisConfig.getDatabase())
.setConnectionPoolSize(redisConfig.getConnectionPoolSize())
.setConnectionMinimumIdleSize(redisConfig.getConnectionMinimumIdleSize());
.redissonClient = Redisson.create(config);
}
{
totalAcquireAttempts.incrementAndGet();
System.currentTimeMillis();
{
lockCache.computeIfAbsent(lockKey, key -> redissonClient.getLock(key));
lock.tryLock(waitTime, timeout, TimeUnit.MILLISECONDS);
(acquired) {
successfulAcquisitions.incrementAndGet();
System.currentTimeMillis() - startTime;
(stats) {
stats.setAverageAcquireTime(
(stats.getAverageAcquireTime() * (successfulAcquisitions.get() - ) + costTime) / successfulAcquisitions.get()
);
stats.getKeyStats().merge(lockKey, , Long::sum);
}
log.debug(, lockKey, costTime);
} {
log.warn(, lockKey, waitTime);
}
acquired;
} (InterruptedException e) {
Thread.currentThread().interrupt();
log.error(, lockKey, e);
;
} (Exception e) {
log.error(, lockKey, e);
;
} {
stats.setTotalAcquireAttempts(totalAcquireAttempts.get());
stats.setSuccessfulAcquisitions(successfulAcquisitions.get());
stats.setFailedAcquisitions(totalAcquireAttempts.get() - successfulAcquisitions.get());
}
}
{
{
lockCache.get(lockKey);
(lock != && lock.isHeldByCurrentThread()) {
lock.unlock();
log.debug(, lockKey);
}
} (Exception e) {
log.error(, lockKey, e);
}
}
}
代码清单 4:Redis 分布式锁实现
第四步:自动配置类
@AutoConfiguration
@EnableConfigurationProperties(LockProperties.class)
@ConditionalOnClass(RedissonClient.class)
@ConditionalOnProperty(prefix = "example.lock", name = "enabled", havingValue = "true", matchIfMissing = true)
@AutoConfigureAfter(RedisAutoConfiguration.class)
public class LockAutoConfiguration {
private static final Logger log = LoggerFactory.getLogger(LockAutoConfiguration.class);
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "example.lock", name = "type", havingValue = "redis", matchIfMissing = true)
public DistributedLock redisDistributedLock(LockProperties properties) {
log.info("初始化 Redis 分布式锁,地址:{}", properties.getRedis().getAddress());
return new RedisDistributedLock(properties);
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "example.lock", name = "type", havingValue = "zookeeper")
@ConditionalOnClass(value = {CuratorFramework.class, InterProcessMutex.class})
public DistributedLock zookeeperDistributedLock(LockProperties properties) {
log.info("初始化 Zookeeper 分布式锁,servers: {}", properties.getZookeeper().getServers());
return new ZkDistributedLock(properties);
}
@Bean
@ConditionalOnMissingBean
public LockAspect lockAspect(DistributedLock distributedLock) {
(distributedLock);
}
LockHealthIndicator {
(distributedLock);
}
LockMetrics {
(distributedLock);
}
}
代码清单 5:分布式锁自动配置类
第五步:AOP 切面支持注解式锁
@Aspect
@Component
@ConditionalOnBean(DistributedLock.class)
public class LockAspect {
private final DistributedLock distributedLock;
public LockAspect(DistributedLock distributedLock) {
this.distributedLock = distributedLock;
}
@Around("@annotation(lockable)")
public Object aroundLock(ProceedingJoinPoint joinPoint, Lockable lockable) throws Throwable {
String lockKey = generateLockKey(joinPoint, lockable);
try {
boolean acquired = distributedLock.tryLock(
lockKey, lockable.timeout(), lockable.waitTime()
);
if (!acquired) {
throw new LockAcquireException("获取锁失败:" + lockKey);
}
return joinPoint.proceed();
} finally {
distributedLock.unlock(lockKey);
}
}
private String generateLockKey(ProceedingJoinPoint joinPoint, Lockable lockable) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
(StringUtils.hasText(lockable.key())) {
evaluateSpel(lockable.key(), joinPoint);
}
method.getDeclaringClass().getSimpleName();
method.getName();
Arrays.hashCode(joinPoint.getArgs()) + ;
String.format(, className, methodName, argsHash);
}
String {
expression;
}
}
代码清单 6:分布式锁 AOP 切面
第六步:健康检查
@Component
public class LockHealthIndicator implements HealthIndicator {
private final DistributedLock distributedLock;
public LockHealthIndicator(DistributedLock distributedLock) {
this.distributedLock = distributedLock;
}
@Override
public Health health() {
DistributedLock.LockStats stats = distributedLock.getStats();
double successRate = stats.getTotalAcquireAttempts() > 0 ?
(double) stats.getSuccessfulAcquisitions() / stats.getTotalAcquireAttempts() * 100 : 0;
Map<String, Object> details = new HashMap<>();
details.put("successRate", String.format("%.2f%%", successRate));
details.put("totalAttempts", stats.getTotalAcquireAttempts());
details.put("successCount", stats.getSuccessfulAcquisitions());
details.put("avgAcquireTime", stats.getAverageAcquireTime() + "ms");
if (successRate < 95.0) {
return Health.down()
.withDetail("message", "锁获取成功率过低")
.withDetails(details)
.build();
}
return Health.up()
.withDetails(details)
.build();
}
}
代码清单 7:分布式锁健康检查
3.4 配置元数据:让 IDE 智能提示
在 src/main/resources/META-INF/ 下创建:
{
"properties": [
{
"name": "example.lock.enabled",
"type": "java.lang.Boolean",
"description": "是否启用分布式锁",
"defaultValue": true
},
{
"name": "example.lock.type",
"type": "java.lang.String",
"description": "锁类型:redis 或 zookeeper",
"defaultValue": "redis"
},
{
"name": "example.lock.default-timeout",
"type": "java.lang.Long",
"description": "默认锁超时时间(毫秒)",
代码清单 8:配置元数据
3.5 默认配置文件
在 src/main/resources/ 下创建 application-lock.yml:
example:
lock:
enabled: true
type: redis
default-timeout: 30000
default-wait-time: 10000
retry-times: 3
redis:
address: ${REDIS_HOST:redis://localhost:6379}
password: ${REDIS_PASSWORD:}
database: 0
connection-pool-size: 64
connection-minimum-idle-size: 24
zookeeper:
servers: ${ZK_HOST:localhost:2181}
session-timeout: 30000
connection-timeout: 15000
namespace: example-lock
代码清单 9:默认配置文件
4. Starter 的使用:简单到哭
4.1 Maven 依赖
<dependency>
<groupId>com.example</groupId>
<artifactId>distributed-lock-spring-boot-starter</artifactId>
<version>1.0.0</version>
</dependency>
4.2 配置(可选)
example:
lock:
type: redis
redis:
address: redis://prod-redis:6379
default-timeout: 60000
4.3 使用方式
方式一:注解式(推荐)
@Service
public class OrderService {
@Lockable(key = "'order:' + #orderId", timeout = 30000)
public Order createOrder(String orderId, OrderRequest request) {
return orderRepository.save(convertToOrder(request));
}
@Lockable(key = "'inventory:' + #productId", waitTime = 5000)
public void reduceInventory(String productId, int quantity) {
inventoryService.reduce(productId, quantity);
}
}
方式二:编程式
@Service
public class PaymentService {
@Autowired
private DistributedLock distributedLock;
public void processPayment(String paymentId) {
String lockKey = "payment:" + paymentId;
try {
if (distributedLock.tryLock(lockKey)) {
paymentProcessor.process(paymentId);
} else {
throw new BusinessException("系统繁忙,请稍后重试");
}
} finally {
distributedLock.unlock(lockKey);
}
}
}
4.4 监控查看
启动应用后,可以访问:
- 健康检查:
/actuator/health(查看锁的健康状态)
- 监控指标:
/actuator/metrics/example.lock(查看锁的统计指标)
- 详细信息:
/actuator/lock-stats(如果有自定义 Endpoint)
5. 企业级 Starter 的高级特性
5.1 多版本兼容:向前向后都要考虑
Starter 一旦被多个服务使用,版本兼容就是大问题。我的经验:
版本策略
<version>主版本。次版本。修订版本 - 里程碑</version>
<version>1.0.0</version>
<version>1.1.0</version>
<version>2.0.0</version>
兼容性保证
- 配置属性兼容:新增属性要有默认值,删除属性要提供迁移期
- API 兼容:公共接口不轻易修改,用@Deprecated 标记过时方法
- 依赖兼容:第三方依赖版本要谨慎升级
public interface DistributedLock {
boolean tryLock(String lockKey, long timeout, long waitTime);
default boolean tryLock(String lockKey, LockOptions options) {
return tryLock(lockKey, options.getTimeout(), options.getWaitTime());
}
@Deprecated(since = "2.0.0", forRemoval = true)
boolean oldMethod(String param);
}
5.2 性能优化:Starter 不能拖慢应用
优化点一:懒加载
@Bean
@Lazy
public DistributedLock distributedLock(LockProperties properties) {
}
优化点二:连接池复用
@Configuration
public class LockAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public RedissonClient redissonClient(LockProperties properties) {
}
}
优化点三:缓存优化
public class RedisDistributedLock {
private final Map<String, WeakReference<RLock>> lockCache = Collections.synchronizedMap(new WeakHashMap<>());
}
5.3 监控告警:出了问题要知道
生产级的 Starter 必须有完整的监控:
@Component
public class LockMetrics implements MeterBinder {
private final DistributedLock distributedLock;
public LockMetrics(DistributedLock distributedLock) {
this.distributedLock = distributedLock;
}
@Override
public void bindTo(MeterRegistry registry) {
Gauge.builder("example.lock.success.rate", () -> calculateSuccessRate())
.description("锁获取成功率")
.baseUnit("percent")
.register(registry);
Timer.builder("example.lock.acquire.time")
.description("锁获取耗时")
.publishPercentiles(0.5, 0.95, 0.99)
.register(registry);
}
private double calculateSuccessRate() {
DistributedLock.LockStats stats = distributedLock.getStats();
if (stats.getTotalAcquireAttempts() == 0) {
return 100.0;
}
return (double) stats.getSuccessfulAcquisitions() / stats.getTotalAcquireAttempts() * 100;
}
}
告警规则示例(Prometheus):
groups:
- name: lock_alerts
rules:
- alert: LockSuccessRateLow
expr: example_lock_success_rate < 95
for: 5m
labels:
severity: warning
annotations:
summary: "锁获取成功率过低"
description: "锁获取成功率低于 95%,当前值:{{ $value }}%"
6. Starter 的测试策略
6.1 单元测试:保证代码质量
@ExtendWith(MockitoExtension.class)
class RedisDistributedLockTest {
@Mock
private RedissonClient redissonClient;
@Mock
private RLock rLock;
private RedisDistributedLock distributedLock;
@BeforeEach
void setUp() {
LockProperties properties = new LockProperties();
properties.setType("redis");
when(redissonClient.getLock(anyString())).thenReturn(rLock);
distributedLock = new RedisDistributedLock(properties);
setField(distributedLock, "redissonClient", redissonClient);
}
@Test
void testTryLock_Success() throws InterruptedException {
when(rLock.tryLock(anyLong(), anyLong(), any())).thenReturn(true);
boolean result = distributedLock.tryLock("test-key", 30000, 10000);
assertTrue(result);
verify(rLock).tryLock(10000, 30000, TimeUnit.MILLISECONDS);
}
@Test
void testTryLock_Timeout() throws InterruptedException {
(rLock.tryLock(anyLong(), anyLong(), any())).thenReturn();
distributedLock.tryLock(, , );
assertFalse(result);
}
}
6.2 集成测试:验证真实环境
@SpringBootTest
@AutoConfigureMockMvc
@Testcontainers
class DistributedLockIntegrationTest {
@Container
static RedisContainer redis = new RedisContainer("redis:6.2")
.withExposedPorts(6379);
@DynamicPropertySource
static void redisProperties(DynamicPropertyRegistry registry) {
registry.add("example.lock.redis.address", () -> String.format("redis://%s:%d", redis.getHost(), redis.getFirstMappedPort()));
}
@Autowired
private DistributedLock distributedLock;
@Test
void testDistributedLockInRealRedis() {
String lockKey = "integration-test-key";
boolean acquired = distributedLock.tryLock(lockKey, 5000, 1000);
assertTrue(acquired);
boolean acquiredAgain = distributedLock.tryLock(lockKey, 5000, 1000);
assertFalse(acquiredAgain);
distributedLock.unlock(lockKey);
distributedLock.tryLock(lockKey, , );
assertTrue(acquiredAfterRelease);
}
}
6.3 性能测试:确保不影响应用性能
@SpringBootTest
@Tag("performance")
class DistributedLockPerformanceTest {
@Autowired
private DistributedLock distributedLock;
@Test
@RepeatedTest(10)
void testLockAcquirePerformance() {
IntStream.range(0, 1000).parallel().forEach(i -> {
distributedLock.tryLock("perf-" + i, 1000, 100);
});
long startTime = System.nanoTime();
IntStream.range(0, 10000).parallel().forEach(i -> {
distributedLock.tryLock("perf-" + i, 1000, 100);
});
long duration = System.nanoTime() - startTime;
double avgTime = duration / 10000.0 / 1_000_000.0;
System.out.printf("平均获取锁时间:%.3f ms%n", avgTime);
assertTrue(avgTime < 1.0, "锁获取时间过长:" + avgTime + "ms");
}
}
7. Starter 的发布与治理
7.1 Maven 发布配置
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>distributed-lock-spring-boot-starter</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<name>Example Distributed Lock Starter</name>
<description>Spring Boot Starter for distributed lock</description>
<licenses>
<license>
<name>Apache License, Version 2.0</name>
<url>https://www.apache.org/licenses/LICENSE-2.0</url>
</license>
</licenses>
<scm>
<url>https://github.com/example/distributed-lock-starter</url>
<connection>scm:git:git://github.com/example/distributed-lock-starter.git
scm:git:ssh://github.com/example/distributed-lock-starter.git
Your Name
[email protected]
Example Inc.
example-releases
https://maven.example.com/repository/releases
example-snapshots
https://maven.example.com/repository/snapshots
7.2 版本管理策略
我推荐使用语义化版本(Semantic Versioning):

图 2:语义化版本管理流程
实际例子:
1.0.0:第一个稳定版本
1.0.1:修复 bug
1.1.0:新增功能,向后兼容
2.0.0:破坏性变更
7.3 依赖管理
Starter 的依赖管理要特别小心:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
org.redisson
redisson-spring-boot-starter
provided
true
org.springframework.boot
spring-boot-starter-test
test
关键点:可选依赖用 <optional>true</optional>,这样用户的项目不会强制引入这些依赖。
8. 企业级 Starter 架构演进

8.1 从单一 Starter 到 Starter 套件
当 Starter 多了之后,就需要考虑架构了。在大型企业中,我们是这样组织的:
example-spring-boot-starters/
├── example-spring-boot-starter-parent
├── example-spring-boot-starter-lock
├── example-spring-boot-starter-id
├── example-spring-boot-starter-mq
├── example-spring-boot-starter-cache
├── example-spring-boot-starter-trace
└── example-spring-boot-starter-all
8.2 Starter 的依赖关系管理
复杂的 Starter 之间可能有依赖关系,需要仔细管理:
<dependencies>
<dependency>
<groupId>com.example</groupId>
<artifactId>example-spring-boot-starter-lock</artifactId>
<version>${project.version}</version>
<optional>true</optional>
</dependency>
</dependencies>
8.3 配置的统一管理
多个 Starter 可能有相同的配置项(比如 Redis 地址),需要统一:
@ConfigurationProperties(prefix = "example")
public class ExampleCommonProperties {
private RedisCommonConfig redis = new RedisCommonConfig();
private ZkCommonConfig zookeeper = new ZkCommonConfig();
private MonitorConfig monitor = new MonitorConfig();
@Data
public static class RedisCommonConfig {
private String address = "redis://localhost:6379";
private String password;
private int database = 0;
}
}
@ConfigurationProperties(prefix = "example.lock")
public class LockProperties {
@Autowired
private ExampleCommonProperties commonProperties;
String {
StringUtils.hasText(redis.getAddress()) ? redis.getAddress() : commonProperties.getRedis().getAddress();
}
}
9. 故障排查与调试
9.1 常见问题排查清单
我总结了 Starter 开发中最常见的 10 个问题:
问题 1:自动配置不生效
排查步骤:
- 检查
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件
- 检查条件注解是否满足
- 检查是否有其他 Starter 排除
java -jar app.jar --debug
@Autowired
private List<String> autoConfigurations;
问题 2:Bean 冲突
解决方案:
@Bean
@ConditionalOnMissingBean
public SomeBean someBean() {
return new SomeBean();
}
@Bean
@Primary
public SomeBean defaultSomeBean() {
return new SomeBean();
}
问题 3:配置属性不生效
排查:
- 检查
spring-configuration-metadata.json 格式
- 检查属性前缀是否正确
- 检查是否有其他配置覆盖
@PostConstruct
public void validateConfig() {
if (!isValid(config)) {
throw new IllegalStateException("配置无效:" + config);
}
}
9.2 调试技巧
技巧 1:在 IDE 中调试自动配置
@AutoConfiguration
public class MyAutoConfiguration {
public MyAutoConfiguration() {
System.out.println("MyAutoConfiguration 被加载");
}
}
技巧 2:查看条件评估报告
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(Application.class);
app.addListeners(new ApplicationListener<ApplicationPreparedEvent>() {
@Override
public void onApplicationEvent(ApplicationPreparedEvent event) {
ConditionEvaluationReport report = ConditionEvaluationReport.get(event.getApplicationContext().getBeanFactory());
try (PrintWriter writer = new PrintWriter("condition-report.txt")) {
report.getConditionAndOutcomesBySource().forEach((source, outcomes) -> {
writer.println(source);
outcomes.forEach(outcome -> {
writer.println(" " + outcome.getOutcome());
});
});
}
}
});
app.run(args);
}
}
技巧 3:使用 Spring Boot Actuator 端点
management:
endpoints:
web:
exposure:
include: conditions,beans,configprops
然后访问:
/actuator/conditions:查看条件评估详情
/actuator/beans:查看所有 Bean
/actuator/configprops:查看配置属性
10. 最后的话:Starter 开发的"道"与"术"
写了这么多年 Starter,我最大的体会是:技术是术,设计思想才是道。
好的 Starter 不是代码堆砌,而是对开发者体验的极致追求。你看 Spring Boot 官方的 Starter,每一个都经过精心设计:
- 开箱即用:默认配置就能工作
- 配置简单:几个属性就能满足大部分需求
- 扩展灵活:可以深度定制
- 文档完整:有明确的指导
所以,在你动手写 Starter 之前,先问自己几个问题:
- 🤔 这个 Starter 解决了什么痛点?
- 🤔 用户使用起来方便吗?
- 🤔 配置项是不是太多了?
- 🤔 向后兼容怎么保证?
- 🤔 出了问题怎么排查?
记住:你写的不是代码,是生产力工具。好的 Starter 能让团队效率翻倍,坏的 Starter 能让团队陷入泥潭。
📚 推荐阅读
官方文档
- Spring Boot 官方文档 - Creating Your Own Auto-configuration - 官方指南,必读
- Spring Boot Starter 开发指南 - 实操教程
源码学习
- Spring Boot Auto-configure 源码 - 学习官方实现
- Awesome Spring Boot Starters - 官方 Starter 列表
最佳实践
- 阿里巴巴 Java 开发手册 - 工程规约 - 命名规范、配置规范
- Spring Boot 最佳实践 - 官方最佳实践
工具资源
- Spring Initializr - 快速生成 Starter 项目
- Maven Central - 查找依赖和版本
最后建议:别光看,动手写一个!从最简单的开始,比如一个 hello-spring-boot-starter,让它根据配置输出不同的问候语。写完了发布到公司的 Maven 仓库,让同事用起来。实战一次,胜过看百篇文章。