Spring-Boot-Useful-Function
前言
我们每天都在用SpringBoot,但可能只用到了它20%的功能。今天我要分享那些让开发效率提升数倍的隐藏神器,希望对你会有所帮助。
一、@Conditional注解
有些小伙伴在工作中可能遇到过这样的场景:不同环境需要加载不同的Bean配置。传统的做法是用@Profile,但@Conditional提供了更灵活的控制能力。
基础用法
@Configuration
public class DataSourceConfig {
@Bean
@Conditional(ProdDataSourceCondition.class)
public DataSource prodDataSource() {
// 生产环境数据源
return DataSourceBuilder.create()
.url("jdbc:mysql://prod-host:3306/app")
.username("prod-user")
.password("prod-pass")
.build();
}
@Bean
@Conditional(DevDataSourceCondition.class)
public DataSource devDataSource() {
// 开发环境数据源
return DataSourceBuilder.create()
.url("jdbc:h2:mem:testdb")
.username("sa")
.password("")
.build();
}
}
// 生产环境条件判断
public class ProdDataSourceCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String env = context.getEnvironment().getProperty("app.env");
return"prod".equals(env);
}
}
// 开发环境条件判断
public class DevDataSourceCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String env = context.getEnvironment().getProperty("app.env");
return"dev".equals(env) || env == null;
}
}
进阶用法:组合条件
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnDatabaseTypeCondition.class)
public @interface ConditionalOnDatabaseType {
String value();
}
public class OnDatabaseTypeCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Map<String, Object> attributes = metadata.getAnnotationAttributes(
ConditionalOnDatabaseType.class.getName());
String expectedType = (String) attributes.get("value");
String actualType = context.getEnvironment().getProperty("app.db.type");
return expectedType.equals(actualType);
}
}
// 使用自定义条件注解
@Configuration
public class CacheConfig {
@Bean
@ConditionalOnDatabaseType("redis")
public CacheManager redisCacheManager() {
return new RedisCacheManager();
}
@Bean
@ConditionalOnDatabaseType("caffeine")
public CacheManager caffeineCacheManager() {
return new CaffeineCacheManager();
}
}
深度解析:
@Conditional的核心价值在于实现了"条件化配置",这是SpringBoot自动配置的基石。通过实现Condition接口,我们可以基于任何条件(环境变量、系统属性、类路径、Bean存在性等)来决定是否加载某个Bean。
二、@ConfigurationProperties
有些小伙伴可能还在用@Value一个个注入配置属性,其实@ConfigurationProperties才是更优雅的解决方案。
基础绑定
@Component
@ConfigurationProperties(prefix = "app.datasource")
@Validated
public class DataSourceProperties {
@NotBlank
private String url;
@NotBlank
private String username;
private String password;
@Min(1)
@Max(100)
private int maxPoolSize = 10;
private Duration connectionTimeout = Duration.ofSeconds(30);
// 嵌套配置
private Pool pool = new Pool();
// getters and setters
public static class Pool {
private int maxSize = 20;
private int minIdle = 5;
// getters and setters
}
}
// application.yml
app:
datasource:
url: jdbc:mysql://localhost:3306/test
username: root
password: secret
max-pool-size: 20
connection-timeout: 60s
pool:
max-size: 50
min-idle: 10
类型安全配置
@Configuration
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public DataSource dataSource(DataSourceProperties properties) {
HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl(properties.getUrl());
dataSource.setUsername(properties.getUsername());
dataSource.setPassword(properties.getPassword());
dataSource.setMaximumPoolSize(properties.getMaxPoolSize());
dataSource.setConnectionTimeout(properties.getConnectionTimeout().toMillis());
return dataSource;
}
}
深度解析:
@ConfigurationProperties不仅提供了类型安全的配置绑定,还支持嵌套属性、集合类型、数据校验、宽松绑定(kebab-case到camelCase自动转换)等特性。这是SpringBoot"约定优于配置"理念的完美体现。
三、Spring Boot Actuator
生产环境监控是系统稳定性的生命线,Actuator提供了开箱即用的监控端点。
核心端点配置
@Configuration
public class ActuatorConfig {
// 自定义健康检查
@Component
public class DatabaseHealthIndicator implements HealthIndicator {
@Autowired
private DataSource dataSource;
@Override
public Health health() {
try (Connection conn = dataSource.getConnection()) {
if (conn.isValid(1000)) {
return Health.up()
.withDetail("database", "Available")
.withDetail("validationQuery", "SUCCESS")
.build();
}
} catch (SQLException e) {
return Health.down(e)
.withDetail("database", "Unavailable")
.withDetail("error", e.getMessage())
.build();
}
return Health.unknown().build();
}
}
// 自定义指标
@Component
public class OrderMetrics {
private final Counter orderCounter;
private final DistributionSummary orderAmountSummary;
public OrderMetrics(MeterRegistry registry) {
this.orderCounter = Counter.builder("order.count")
.description("Total number of orders")
.register(registry);
this.orderAmountSummary = DistributionSummary.builder("order.amount")
.description("Order amount distribution")
.baseUnit("USD")
.register(registry);
}
public void recordOrder(Order order) {
orderCounter.increment();
orderAmountSummary.record(order.getAmount().doubleValue());
}
}
}
// application.yml 管理端 点暴露配置
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
endpoint:
health:
show-details: always
show-components: always
metrics:
enabled: true
自定义信息端点
@Component
public class BuildInfoContributor implements InfoContributor {
@Override
public void contribute(Info.Builder builder) {
Map<String, String> buildDetails = new HashMap<>();
buildDetails.put("version", "1.0.0");
buildDetails.put("timestamp", Instant.now().toString());
buildDetails.put("commit", getGitCommit());
builder.withDetail("build", buildDetails)
.withDetail("environment", getEnvironmentInfo());
}
private String getGitCommit() {
// 获取Git提交信息
try {
return new String(Files.readAllBytes(Paths.get("git.properties")));
} catch (IOException e) {
return"unknown";
}
}
}
深度解析:
Actuator不仅仅是监控工具,它提供了应用的全方位可观测性。通过健康检查、指标收集、审计事件、HTTP追踪等功能,我们可以构建完整的应用监 控体系。
四、Spring Boot DevTools
有些小伙伴可能还在手动重启应用来查看代码变更效果,DevTools提供了极致的开发体验。
热加载配置
// application-dev.yml
spring:
devtools:
restart:
enabled: true
exclude: static/**,public/**
additional-paths: src/main/java
livereload:
enabled: true
thymeleaf:
cache: false
freemarker:
cache: false
// 自定义重启触发器
@Component
public class CustomRestartTrigger implements ApplicationListener<ClassPathChangedEvent> {
private final RestartScope restartScope;
public CustomRestartTrigger(RestartScope restartScope) {
this.restartScope = restartScope;
}
@Override
public void onApplicationEvent(ClassPathChangedEvent event) {
if (event.getChangeSet().isModified()) {
// 清除重启范围内的Bean
restartScope.clear();
System.out.println("检测到类路径变化,准备重启...");
}
}
}
开发时配置覆盖
// 开发环境特定配置
@Profile("dev")
@Configuration
public class DevConfig {
@Bean
public SomeService someService() {
// 返回mock实现或开发环境特定实现
return new MockSomeService();
}
}
深度解析:
DevTools通过类加载器技巧实现了快速应用重启,同时提供了LiveReload、全局配置、开发时属性覆盖等功能,将开发效率提升到了新的高度。
五、Spring Retry
分布式系统中,网络抖动、服务短暂不可用是常态。Spring Retry提供了声明式的重试解决方案。
基础重试配置
@Service
public class PaymentService {
@Retryable(
value = {PaymentException.class, NetworkException.class},
maxAttempts = 3,
backoff = @Backoff(delay = 1000, multiplier = 2)
)
public PaymentResult processPayment(PaymentRequest request) {
// 调用支付网关
return paymentGateway.process(request);
}
@Recover
public PaymentResult recover(PaymentException e, PaymentRequest request) {
// 重试全部失败后的恢复逻辑
log.error("支付处理失败,进入恢复逻辑", e);
return PaymentResult.failed("支付处理暂时不可用");
}
}
// 配置类
@Configuration
@EnableRetry
public class RetryConfig {
@Bean
public RetryTemplate retryTemplate() {
return RetryTemplate.builder()
.maxAttempts(5)
.exponentialBackoff(1000, 2, 10000)
.retryOn(RemoteAccessException.class)
.traversingCauses()
.build();
}
}
高级重试策略
@Component
public class CircuitBreakerRetryListener extends RetryListenerSupport {
private final CircuitBreaker circuitBreaker;
public CircuitBreakerRetryListener() {
this.circuitBreaker = CircuitBreaker.ofDefaults("payment-service");
}
@Override
public <T, E extends Throwable> void onError(
RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
// 记录失败,可能触发熔断
circuitBreaker.onError(throwable);
if (circuitBreaker.tryAcquirePermission()) {
log.warn("重试失败,但熔断器仍允许继续尝试");
} else {
log.error("重试失败,熔断器已打开,停止重试");
context.setExhaustedOnly(); // 标记为耗尽,停止重试
}
}
}