fix(stream): 缺 StringRedisTemplate Bean 时自动装配优雅退场,对齐 cache

stream 装配此前仅 @ConditionalOnClass,缺 StringRedisTemplate Bean 时
(如 exclude RedisAutoConfiguration)会因构造参数缺依赖而启动失败。加
@ConditionalOnBean(StringRedisTemplate) 使整类 back off,与 cache 一致;
顺带 @Configuration(proxyBeanMethods=false) 对齐风格。补 3 个装配测试。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
root
2026-06-26 11:41:56 +08:00
parent 7101a1fa65
commit e30faf4cff
2 changed files with 74 additions and 1 deletions

View File

@@ -8,6 +8,7 @@ import com.njcn.middle.stream.support.StreamTrimScheduler;
import com.njcn.middle.stream.template.RedisStreamEnhanceTemplate; import com.njcn.middle.stream.template.RedisStreamEnhanceTemplate;
import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
@@ -26,8 +27,9 @@ import java.util.stream.Collectors;
* Phase 2 由 message-boot 注入 Processor 即生效。装配在 {@link RedisAutoConfiguration} 之后, * Phase 2 由 message-boot 注入 Processor 即生效。装配在 {@link RedisAutoConfiguration} 之后,
* 确保 {@link StringRedisTemplate} 已就绪。 * 确保 {@link StringRedisTemplate} 已就绪。
*/ */
@Configuration @Configuration(proxyBeanMethods = false)
@ConditionalOnClass(StringRedisTemplate.class) @ConditionalOnClass(StringRedisTemplate.class)
@ConditionalOnBean(StringRedisTemplate.class)
@AutoConfigureAfter(RedisAutoConfiguration.class) @AutoConfigureAfter(RedisAutoConfiguration.class)
@EnableConfigurationProperties(RedisStreamProperties.class) @EnableConfigurationProperties(RedisStreamProperties.class)
public class RedisStreamAutoConfiguration { public class RedisStreamAutoConfiguration {

View File

@@ -0,0 +1,71 @@
package com.njcn.middle.stream.autoconfig;
import com.njcn.middle.stream.container.StreamAutoClaimer;
import com.njcn.middle.stream.container.StreamListenerContainer;
import com.njcn.middle.stream.support.StreamKeyBuilder;
import com.njcn.middle.stream.support.StreamTrimScheduler;
import com.njcn.middle.stream.template.RedisStreamEnhanceTemplate;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
/**
* 自动装配测试:验证 stream starter 的 Bean 装配、用户自定义回退、以及缺 {@link StringRedisTemplate}
* 时整类优雅退场(与 cache 装配口径对齐,不因缺依赖启动失败)。
*/
class RedisStreamAutoConfigurationTest {
private final ApplicationContextRunner runner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(
RedisAutoConfiguration.class, RedisStreamAutoConfiguration.class))
.withPropertyValues("spring.redis.host=127.0.0.1", "spring.redis.port=6379");
@Test
void registersStreamBeans() {
runner.run(ctx -> {
assertThat(ctx).hasSingleBean(StreamKeyBuilder.class);
assertThat(ctx).hasSingleBean(RedisStreamEnhanceTemplate.class);
assertThat(ctx).hasSingleBean(StreamListenerContainer.class);
assertThat(ctx).hasSingleBean(StreamAutoClaimer.class);
assertThat(ctx).hasSingleBean(StreamTrimScheduler.class);
});
}
@Test
void backsOffWhenUserDefinesOwnTemplate() {
runner.withUserConfiguration(CustomConfig.class).run(ctx -> {
assertThat(ctx).hasSingleBean(RedisStreamEnhanceTemplate.class);
assertThat(ctx.getBean(RedisStreamEnhanceTemplate.class))
.isSameAs(ctx.getBean("myTemplate"));
});
}
@Test
void backsOffWhenNoStringRedisTemplateBean() {
// 不装配 RedisAutoConfiguration,上下文内无 StringRedisTemplate Bean:
// 应整类 back off 优雅退场,而非因缺依赖启动失败(与 cache 装配口径一致)。
new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(RedisStreamAutoConfiguration.class))
.run(ctx -> assertThat(ctx)
.hasNotFailed()
.doesNotHaveBean(RedisStreamEnhanceTemplate.class));
}
@Configuration
static class CustomConfig {
@Bean
RedisStreamEnhanceTemplate myTemplate() {
return new RedisStreamEnhanceTemplate(
new StringRedisTemplate(mock(RedisConnectionFactory.class)),
new StreamKeyBuilder(false, ""));
}
}
}