在Project Reactor中为不同算子下游保留不同类型的方案咨询
兄弟,我太懂你这种纠结了——想让不同算子拿到各自需要的类型,又不想硬塞个大容器显得设计臃肿,更不想平白无故多查一次User,这折腾劲儿谁遇着都头疼!刚好我之前也处理过类似的场景,给你几个实用又优雅的方案:
用框架原生的
Tuple轻量持有,避免自定义容器的冗余
Reactor本身就提供了Tuples.of()这种元组工具,不用你自己写个大而全的容器类,下游算子可以直接通过getT1()、getT2()分别拿到DTO和User,语义清晰还没有额外设计负担。而且User只需要加载一次,完美符合你的要求。举个实际代码例子:// 假设你已经有获取DTO和User的流 Mono<Dto> dtoMono = fetchDtoById(id); Mono<User> userMono = fetchUserById(userId); // 合并成Tuple2,下游就能同时拿到两者 Mono<Tuple2<Dto, User>> combinedMono = Mono.zip(dtoMono, userMono); combinedMono.flatMap(tuple -> { Dto targetDto = tuple.getT1(); User currentUser = tuple.getT2(); // 这里可以同时用两个对象做业务操作 return businessService.process(targetDto, currentUser); }) .doOnNext(result -> { // 下游如果只需要其中一个类型,直接从元组提取就行 });用Reactor的
Context做上下文传递,适合全局复用的场景
如果你的User是类似“当前登录用户”这种全局上下文信息,完全可以把它放到Reactor的Context里,整个流的上下游都能随时取出,不用在数据流里显式传递,这样数据流里只需要带业务核心的DTO就行,不会被额外的上下文类型污染。代码示例:Mono<Dto> dtoMono = fetchDtoById(id); // 从Context中取出User和DTO配合处理 dtoMono.transformDeferredWithContext((dtoFlux, contextView) -> { User currentUser = contextView.get(User.class); return dtoFlux.flatMap(dto -> businessService.process(dto, currentUser)); }) // 提前将User存入Context .contextWrite(context -> context.put(User.class, loginUser));自定义极简的语义化载体(比元组更易维护)
如果你觉得Tuple的getT1()、getT2()语义不够明确,怕后续维护的同事看不懂,那可以写个极度轻量的record(Java 16+支持),只存当前流程需要的两个对象,没有任何多余逻辑,完全不存在设计臃肿的问题:// 纯载体,无任何业务逻辑 public record ProcessContext(Dto dto, User user) {}然后合并流的时候转成这个record:
Mono<ProcessContext> contextMono = Mono.zip(dtoMono, userMono) .map(tuple -> new ProcessContext(tuple.getT1(), tuple.getT2()));这样下游算子一看
ProcessContext就知道里面是当前流程需要的两个对象,语义清晰,维护成本极低。
其实你之前觉得“用容器设计不好”,大概率是怕搞那种包罗万象的大DTO,但只要是为当前特定流程定制的轻量载体,不管是框架原生的元组还是自己写的极简record,都是非常合理的设计,完全不会有冗余问题,还能彻底避免重复加载User的麻烦。
内容来源于stack exchange

