SpringBoot Starter自定义开发
Spring Boot 简化了配置,但日志管理依然需要重视。日志配置、链路追踪、排查思路都是日常开发中会遇到的问题。本文讲实际项目中的日志管理经验。
Starter 命名规范
| 类型 |
命名格式 |
示例 |
| 官方 Starter |
spring-boot-starter-* |
spring-boot-starter-web |
| 第三方 Starter |
*-spring-boot-starter |
mybatis-spring-boot-starter |
开发步骤
#
1. 创建 Maven 项目
<project> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.x</version> </parent> <artifactId>my-service-spring-boot-starter</artifactId> <version>1.0.0</version> <dependencies> <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> </dependencies> </project>
|
#
2. 创建配置属性类
@ConfigurationProperties(prefix = "my.service") public class MyServiceProperties {
private boolean enabled = true;
private String name = "default";
private int timeout = 5000;
private int retry = 3; public boolean isEnabled() { return enabled; } public void setEnabled(boolean enabled) { this.enabled = enabled; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getTimeout() { return timeout; } public void setTimeout(int timeout) { this.timeout = timeout; } public int getRetry() { return retry; } public void setRetry(int retry) { this.retry = retry; } }
|
#
3. 创建核心服务类
public class MyService { private final MyServiceProperties properties; public MyService(MyServiceProperties properties) { this.properties = properties; } public String sayHello(String name) { return String.format("[%s] Hello, %s!", properties.getName(), name); } public void executeWithRetry(Runnable task) { int attempts = 0; while (attempts < properties.getRetry()) { try { task.run(); return; } catch (Exception e) { attempts++; if (attempts >= properties.getRetry()) { throw new RuntimeException("重试耗尽", e); } } } } }
|
#
4. 创建自动配置类
@Configuration @ConditionalOnClass(MyService.class) @ConditionalOnProperty(prefix = "my.service", name = "enabled", havingValue = "true", matchIfMissing = true) @EnableConfigurationProperties(MyServiceProperties.class) public class MyServiceAutoConfiguration { private final MyServiceProperties properties; public MyServiceAutoConfiguration(MyServiceProperties properties) { this.properties = properties; } @Bean @ConditionalOnMissingBean public MyService myService() { return new MyService(properties); } @Bean @ConditionalOnMissingBean @ConditionalOnProperty(prefix = "my.service", name = "health-check", havingValue = "true") public MyHealthIndicator myHealthIndicator(MyService myService) { return new MyHealthIndicator(myService); } }
|
#
5. 配置 spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.example.autoconfigure.MyServiceAutoConfiguration
|
#
6. 配置提示元数据
{ "properties": [ { "name": "my.service.enabled", "type": "java.lang.Boolean", "description": "是否启用 MyService", "defaultValue": true }, { "name": "my.service.name", "type": "java.lang.String", "description": "服务名称" }, { "name": "my.service.timeout", "type": "java.lang.Integer", "description": "超时时间(毫秒)", "defaultValue": 5000 } ] }
|
完整项目结构
my-service-spring-boot-starter/ ├── pom.xml └── src/ └── main/ ├── java/ │ └── com/example/ │ ├── MyService.java │ ├── MyServiceProperties.java │ ├── MyHealthIndicator.java │ └── autoconfigure/ │ └── MyServiceAutoConfiguration.java └── resources/ └── META-INF/ ├── spring.factories └── additional-spring-configuration-metadata.json
|
使用 Starter
#
引入依赖
<dependency> <groupId>com.example</groupId> <artifactId>my-service-spring-boot-starter</artifactId> <version>1.0.0</version> </dependency>
|
#
配置文件
my: service: enabled: true name: myApp timeout: 10000 retry: 5
|
#
注入使用
@Service public class BusinessService { @Autowired private MyService myService; public void doSomething() { String result = myService.sayHello("World"); System.out.println(result); } }
|
高级特性
#
条件装配
@Configuration public class MyServiceAutoConfiguration { @Bean @ConditionalOnClass(RedisTemplate.class) public MyCacheService redisCacheService(RedisTemplate<String, String> redisTemplate) { return new RedisCacheService(redisTemplate); } @Bean @ConditionalOnMissingClass("org.springframework.data.redis.core.RedisTemplate") public MyCacheService localCacheService() { return new LocalCacheService(); } }
|
#
多配置类
@Configuration public class MyServiceAutoConfiguration { @Configuration @ConditionalOnWebApplication public static class WebConfiguration { @Bean public MyWebInterceptor myWebInterceptor() { return new MyWebInterceptor(); } } @Configuration @ConditionalOnNotWebApplication public static class NonWebConfiguration { @Bean public MyCliRunner myCliRunner() { return new MyCliRunner(); } } }
|
测试 Starter
@SpringBootTest(classes = TestApplication.class) public class MyServiceAutoConfigurationTest { @Autowired private MyService myService; @Autowired private MyServiceProperties properties; @Test public void testServiceCreated() { assertNotNull(myService); } @Test public void testPropertiesLoaded() { assertEquals("myApp", properties.getName()); assertEquals(10000, properties.getTimeout()); } }
@SpringBootApplication @EnableConfigurationProperties(MyServiceProperties.class) class TestApplication { }
|
发布到 Maven 仓库
<distributionManagement> <repository> <id>releases</id> <url>http://nexus.example.com/repository/maven-releases/</url> </repository> </distributionManagement>
|
总结
开发 Spring Boot Starter 的关键步骤:
- 创建项目:引入 spring-boot-autoconfigure
- 定义属性:@ConfigurationProperties
- 实现功能:核心服务类
- 自动配置:@Configuration + 条件注解
- 注册配置:META-INF/spring.factories
- IDE 提示:配置元数据
自定义 Starter 可以将团队的公共能力沉淀为标准化组件,提高开发效率和代码复用。
核心要点
日志级别设置:根据环境设置合适的级别
日志格式配置:添加 traceId 便于链路追踪
日志输出:控制台输出和文件输出的配置
日志归档:设置滚动策略和保留时间
总结
日志是排查问题的生命线,合理配置日志可以提升排查效率。在实际项目中,结合 ELK 等工具搭建日志系统,可以更好地管理和分析日志。