fix(cache): 构造期校验配置项,误配在装配阶段即 fail-fast
default/null-ttl-seconds、jitter-ratio、breakdown 的 lock-ttl-ms/wait-timeout-ms/ wait-interval-ms 原无校验,负值/越界要到运行期(写入非法 TTL、抖动放大数倍)才暴露。 在 RedisCacheEnhanceTemplate 构造器手工校验,非法即抛 IllegalArgumentException; 不引 hibernate-validator,守住零新依赖。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -38,6 +38,7 @@ public class RedisCacheEnhanceTemplate {
|
||||
|
||||
public RedisCacheEnhanceTemplate(StringRedisTemplate redis, CacheKeyBuilder keyBuilder,
|
||||
CacheValueCodec codec, RedisCacheProperties props) {
|
||||
validateProps(props);
|
||||
this.redis = redis;
|
||||
this.keyBuilder = keyBuilder;
|
||||
this.codec = codec;
|
||||
@@ -134,6 +135,39 @@ public class RedisCacheEnhanceTemplate {
|
||||
}
|
||||
}
|
||||
|
||||
/** 构造期校验配置项,误配在装配阶段即 fail-fast,而非运行期才暴露。 */
|
||||
private static void validateProps(RedisCacheProperties props) {
|
||||
if (props.getDefaultTtlSeconds() <= 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"redis-cache.default-ttl-seconds must be positive, but was " + props.getDefaultTtlSeconds());
|
||||
}
|
||||
if (props.getNullTtlSeconds() <= 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"redis-cache.null-ttl-seconds must be positive, but was " + props.getNullTtlSeconds());
|
||||
}
|
||||
double ratio = props.getJitterRatio();
|
||||
if (ratio < 0 || ratio >= 1) {
|
||||
throw new IllegalArgumentException(
|
||||
"redis-cache.jitter-ratio must be in [0,1), but was " + ratio);
|
||||
}
|
||||
RedisCacheProperties.Breakdown bd = props.getBreakdown();
|
||||
if (bd == null) {
|
||||
throw new IllegalArgumentException("redis-cache.breakdown must not be null");
|
||||
}
|
||||
if (bd.getLockTtlMs() <= 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"redis-cache.breakdown.lock-ttl-ms must be positive, but was " + bd.getLockTtlMs());
|
||||
}
|
||||
if (bd.getWaitTimeoutMs() < 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"redis-cache.breakdown.wait-timeout-ms must be >= 0, but was " + bd.getWaitTimeoutMs());
|
||||
}
|
||||
if (bd.getWaitIntervalMs() <= 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"redis-cache.breakdown.wait-interval-ms must be positive, but was " + bd.getWaitIntervalMs());
|
||||
}
|
||||
}
|
||||
|
||||
// ---- 读取或回源(三防核心)----
|
||||
|
||||
public <T> T getOrLoad(String key, Class<T> type, Supplier<T> loader) {
|
||||
|
||||
@@ -19,6 +19,7 @@ import org.springframework.data.redis.core.script.RedisScript;
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
@@ -294,4 +295,27 @@ class RedisCacheEnhanceTemplateTest {
|
||||
// 中断后立即停止等待:仅首次 tryGet 读一次,而非空转数百次
|
||||
verify(valueOps, atMost(2)).get("cache:user:1");
|
||||
}
|
||||
|
||||
// ---- #5: 构造期配置 fail-fast 校验 ----
|
||||
|
||||
private RedisCacheEnhanceTemplate newTemplateWith(Consumer<RedisCacheProperties> mut) {
|
||||
RedisCacheProperties p = new RedisCacheProperties();
|
||||
mut.accept(p);
|
||||
return new RedisCacheEnhanceTemplate(redis, keyBuilder, codec, p);
|
||||
}
|
||||
|
||||
@Test
|
||||
void constructorRejectsInvalidProps() {
|
||||
assertThrows(IllegalArgumentException.class, () -> newTemplateWith(p -> p.setDefaultTtlSeconds(0)));
|
||||
assertThrows(IllegalArgumentException.class, () -> newTemplateWith(p -> p.setNullTtlSeconds(-1)));
|
||||
assertThrows(IllegalArgumentException.class, () -> newTemplateWith(p -> p.setJitterRatio(1.0)));
|
||||
assertThrows(IllegalArgumentException.class, () -> newTemplateWith(p -> p.setJitterRatio(-0.1)));
|
||||
assertThrows(IllegalArgumentException.class, () -> newTemplateWith(p -> p.getBreakdown().setLockTtlMs(0)));
|
||||
assertThrows(IllegalArgumentException.class, () -> newTemplateWith(p -> p.getBreakdown().setWaitIntervalMs(0)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void constructorAcceptsDefaults() {
|
||||
new RedisCacheEnhanceTemplate(redis, keyBuilder, codec, new RedisCacheProperties());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user