Lettuce 8.0.0.BETA1 是 8.0 的第一个 Beta,不是正式版,最大的变化是 Project Reactor 不再是必需依赖,只用同步和异步 API 的项目可以把 reactor-core 排除掉。我在 Spring Boot 4.1.1 加 Redis 8.0.2 上排除 reactor-core 试了一遍,连接、同步读写、异步命令和连接事件都正常,调 reactive() 会报 NoClassDefFoundError。
Lettuce 是 Redis 官方维护的 Java 客户端,Spring Data Redis 默认用的就是它。8.0.0.BETA1 在 2026-10-14 19:01(CST)发布到 GitHub,同一天稍早还发了 7.9.0.RELEASE。
官方怎么说
Release 说明的原话是:
This release makes Project Reactor not-required: applications that use only the synchronous and asynchronous APIs can exclude
reactor-core
注意,reactor-core 仍然写在 lettuce-core 的依赖里。What's new 页的原话是 “reactor-core is still a dependency of lettuce-core, but applications that use only sync()/async() can exclude it”。排除以后还需要 Reactor 的只剩三块:reactive API、Kotlin 协程 API,以及已经废弃的 RedisCredentialsProvider。
内部这次也动了不少。连接建立、重连、Sentinel 和主从拓扑发现、事件总线、链路上下文传递,都不再走 Reactor。
破坏性变更
这是大版本,7.8 和 7.9 里标了废弃的 Reactor 相关 API,这次大多删了。挑几处容易碰到的说:
EventBus.get() 删了,它原来返回 Flux<Event>。现在用 subscribe(Consumer) 或 subscribe(Class, Consumer),返回一个 Subscription。
TraceContextProvider.getTraceContextLater() 删了,改成实现 getTraceContextAsync(Map)。
RedisClient.getSocketAddress(RedisURI) 这类连接扩展点,参数从 Mono<SocketAddress> 换成了 Supplier<CompletionStage<SocketAddress>>。自己继承过 RedisClient 或改过连接逻辑的项目要留意。
reactive() 还在,但已经是废弃的默认方法,内部转给 commands(RedisReactiveCommands.factory())。
认证也有个小坑。没有 Reactor 时,给 RedisURI.Builder.withAuthentication(...) 传一个裸 lambda 编译不过,因为两个重载里有一个用到了 Reactor 类型。官方给的办法是传有明确类型的 CredentialsProvider,比如 CredentialsProvider.from(...) 或 StaticCredentialsProvider。
完整列表见官方的 What's new in Lettuce 8.0。
在 Spring Boot 里试一下
环境是 JDK 21.0.12.1、Maven 3.9.9、Spring Boot 4.1.1,本机 Redis 8.0.2。Boot 4.1.1 默认管理的 Lettuce 是 7.5.2.RELEASE,这里用 lettuce.version 属性覆盖成 8.0.0.BETA1。
为了看清 Lettuce 自己的表现,项目没有引入 spring-data-redis,直接把 RedisClient 注册成 Bean。pom 的关键部分:
<properties>
<java.version>21</java.version>
<lettuce.version>8.0.0.BETA1</lettuce.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
<exclusions>
<exclusion>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
先看 mvn dependency:tree 里 lettuce-core 这一支,没有 Reactor:
\- io.lettuce:lettuce-core:jar:8.0.0.BETA1:compile
+- redis.clients.authentication:redis-authx-core:jar:0.2.0:compile
| \- org.slf4j:slf4j-api:jar:2.0.18:compile
+- io.netty:netty-common:jar:4.2.17.Final:compile
+- io.netty:netty-handler:jar:4.2.17.Final:compile
| +- io.netty:netty-resolver:jar:4.2.17.Final:compile
| +- io.netty:netty-buffer:jar:4.2.17.Final:compile
| +- io.netty:netty-transport-native-unix-common:jar:4.2.17.Final:compile
| \- io.netty:netty-codec-base:jar:4.2.17.Final:compile
+- io.netty:netty-transport:jar:4.2.17.Final:compile
\- io.netty:netty-resolver-dns:jar:4.2.17.Final:compile
\- io.netty:netty-codec-dns:jar:4.2.17.Final:compile
入口类 DemoApplication 启动时先确认 Reactor 不在 classpath 上,然后用新的事件总线订阅连接事件,再分别跑同步和异步命令,最后故意调一次 reactive():
package com.example.demo;
import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisURI;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.event.connection.ConnectedEvent;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Bean(destroyMethod = "shutdown")
RedisClient redisClient() {
return RedisClient.create(RedisURI.create("redis://localhost:6379"));
}
@Bean
CommandLineRunner demo(RedisClient client) {
return args -> {
try {
Class.forName("reactor.core.publisher.Mono");
System.out.println("reactor-core on classpath: yes");
} catch (ClassNotFoundException e) {
System.out.println("reactor-core on classpath: no");
}
client.getResources().eventBus().subscribe(ConnectedEvent.class,
e -> System.out.println("event: connected " + e.remoteAddress()));
try (StatefulRedisConnection<String, String> conn = client.connect()) {
conn.sync().set("demo:hello", "lettuce 8");
System.out.println("sync GET demo:hello = " + conn.sync().get("demo:hello"));
Long n = conn.async().incr("demo:counter").toCompletableFuture().get();
System.out.println("async INCR demo:counter = " + n);
try {
conn.reactive();
} catch (Throwable t) {
System.out.println("reactive() -> " + t.getClass().getName() + ": " + t.getMessage());
}
}
};
}
}
打包后用 java -jar target/lettuce8-demo-0.0.1.jar 运行,输出如下:
2026-10-15T08:12:36.951+08:00 INFO 897077 --- [ main] com.example.demo.DemoApplication : Started DemoApplication in 0.711 seconds (process running for 1.01)
reactor-core on classpath: no
event: connected localhost/127.0.0.1:6379
sync GET demo:hello = lettuce 8
async INCR demo:counter = 1
reactive() -> java.lang.NoClassDefFoundError: org/reactivestreams/Publisher
同步 GET、异步 INCR、连接事件都正常。reactive() 抛的是 NoClassDefFoundError,缺的类是 org/reactivestreams/Publisher。和官方说的一致,用 reactive API 就得把 Reactor 加回来。
现在能不能用
这是 Beta,Spring Boot 4.1.1 管理的版本还是 7.x。Spring Data Redis 那边也有 Reactor 相关的代码路径,比如 RedisCredentialsProviderFactory 要的就是 RedisCredentialsProvider。所以上面的示例绕开了 Spring Data Redis,没有验证它和 8.0 Beta 的兼容性。
Release 说明里写了希望大家多试这个 Beta,尤其是排除 Reactor 的用法,在 GA 前反馈。
实测在 Spring Boot 4.1.1 与 Redis 8.0.2 上排除 reactor-core 跑通,对照 8.0.0.BETA1 Release 与 What's new in Lettuce 8.0;同日还有 7.9.0.RELEASE。
一起交流
分享你的思考,让讨论更进一步。