SpringWebFlux(Reactor3)上下文Context处理

更新时间:2023-06-27 19:51:18 阅读: 评论:0

SpringWebFlux(Reactor3)上下⽂Context处理
相关阅读
前⾔
在Spring MVC中我们可以使⽤ThreadLocal保存⼀些信息,作为请求的上下⽂。⼀些公⽤的变量就不⽤写在⽅法中,可以通过上下⽂来获取(⽐如:⽤户的登录信息)。在《重构》中马丁福勒也推荐在能通过其他⽅式获取到的变量时,不要使⽤⽅法参数传递进来。在Spring WebFlux中会存在多次线程切换,ThreadLocal在Spring WebFlux中就⽆法发挥作⽤了,除⾮⼿动的做ThreadLocal线程间的复制,这样处理明显是不明智的⾏为。那在Spring WebFlux中如何处理上下⽂信息,多线程间变量的传递问题?
Reactor上下⽂处理
Spring WebFlux底层使⽤Reactor,在Reactor中有⼀个Context特性,⽤来存放调⽤链的上下⽂信息。在学习Spring WebFlux处理上下⽂信息的⽅式前,我们先了解⼀下Reactor是如何处理的。
Reactor中的Context也是⼀个Map结构,以key-value的形式存储。下⾯通过⼏个来⾃Reactor Guide的例⼦实际看看,Reactor是怎么处理上下⽂的。由于Reactor的Context在3.4.3做了改版,Spring Boot 2.3.3是基于Reactor 3.3+的,所以两个版本的例⼦我都贴上来了。
例⼦1:
3.4.3版本
String key = "message";
Mono<String> r = Mono.just("Hello")
.flatMap(s -> Mono.deferContextual(ctx ->
Mono.just(s + " " + (key))))
.contextWrite(ctx -> ctx.put(key, "World"));
.expectNext("Hello World")
.verifyComplete();
3.3+版本
String key = "message";
Mono<String> r = Mono.just("Hello")
.flatMap( s -> Mono.subscriberContext()
.map( ctx -> s + " " + (key)))
.subscriberContext(ctx -> ctx.put(key, "World"));
.expectNext("Hello World")
.verifyComplete();
我们可以看到contextWrite设置上下⽂的操作在调⽤链的最尾端。在其上游的操作中能成功获取到元素。订阅是从下游流上上游的,看过Reactor或者RxJava源码的同学可能会理解深⼀点,要做到这点只⽤在调⽤next⽅法前将上下⽂设置进去就可以了。
例⼦2:
3.4.3版本
String key = "message";
Mono<String> r = Mono.just("Hello")
.contextWrite(ctx -> ctx.put(key, "World"))
.flatMap( s -> Mono.deferContextual(ctx ->
Mono.just(s + " " + OrDefault(key, "Stranger"))));
.expectNext("Hello Stranger")
.verifyComplete();
3.3+版本
String key = "message";
Mono<String> r = Mono.just("Hello")
.subscriberContext(ctx -> ctx.put(key, "World"))
看的英文.flatMap( s -> Mono.subscriberContext()
.map( ctx -> s + " " + OrDefault(key, "Stranger")));
.expectNext("Hello Stranger")
.verifyComplete();
第⼀个例⼦中contextWrite在调⽤链的最尾端,我们也知道了contextWrite会对上游的操作产⽣影响。那如果放在前⾯呢?第⼆个例⼦将contextWrite放到了调⽤链的中间,可以看到中间的contextWrite没有对后⾯的flatMap产⽣影响。也就是contextWrite只对它上游的操作⽣效,对下游的操作不⽣效。
例⼦3:
3.4.3版本
String key = "message";
Mono<String> r = Mono
.deferContextual(ctx -> Mono.just("Hello " + (key)))
.contextWrite(ctx -> ctx.put(key, "Reactor"))
.contextWrite(ctx -> ctx.put(key, "World"));
回到清朝当驸马.expectNext("Hello Reactor")
.verifyComplete();
3.3+版本
String key = "message";
Mono<String> r = Mono.just("Hello")
.
flatMap( s -> Mono.subscriberContext()
.map( ctx -> s + " " + (key)))
.subscriberContext(ctx -> ctx.put(key, "Reactor"))
.subscriberContext(ctx -> ctx.put(key, "World"));
.expectNext("Hello Reactor")
.verifyComplete();
在前⾯的两个例⼦中,我们看到Reactor的contextWrite只会对其上游的操作⽣效,那如果再下游有多个contextWrite呢?Reactor会从邻近的contextWrite中读取上下⽂的信息。
例⼦4:
3.4.3版本
String key = "message";
Mono<String> r = Mono
.deferContextual(ctx -> Mono.just("Hello " + (key)))
.contextWrite(ctx -> ctx.put(key, "Reactor"))
.flatMap( s -> Mono.deferContextual(ctx ->
Mono.just(s + " " + (key))))
.contextWrite(ctx -> ctx.put(key, "World"));
美国的英文缩写
.expectNext("Hello Reactor World")
.verifyComplete();
3.3+版本
String key = "message";
Mono<String> r = Mono.just("Hello")
.flatMap( s -> Mono.subscriberContext()
.map( ctx -> s + " " + (key)))
.subscriberContext(ctx -> ctx.put(key, "Reactor"))
.flatMap( s -> Mono.subscriberContext()
.map( ctx -> s + " " + (key)))
.subscriberContext(ctx -> ctx.put(key, "World"));香山居士是谁
.expectNext("Hello Reactor World")
.verifyComplete();
这个例⼦中第⼀个获取上下⽂的信息从邻近下游读取到Reactor,第⼆个flatMap邻近下游读取到的World。例⼦5:
3.4.3版本
String key = "message";
Mono<String> r = Mono.just("Hello")
.flatMap( s -> Mono
.deferContextual(ctxView -> Mono.just(s + " " + (key)))
)
.flatMap( s -> Mono
.deferContextual(ctxView -> Mono.just(s + " " + (key)))
.contextWrite(ctx -> ctx.put(key, "Reactor"))
)
.contextWrite(ctx -> ctx.put(key, "World"));
.expectNext("Hello World Reactor")
.verifyComplete();
3.3+版本
String key = "message";
Mono<String> r =
Mono.just("Hello")
.flatMap( s -> Mono.subscriberContext()
.map( ctx -> s + " " + (key))
)
.flatMap( s -> Mono.subscriberContext()
.
map( ctx -> s + " " + (key))
.subscriberContext(ctx -> ctx.put(key, "Reactor"))
)
.subscriberContext(ctx -> ctx.put(key, "World"));
.expectNext("Hello World Reactor")
.verifyComplete();
第⼀个contextWrite只影响内部的操作流。
第⼆个contextWrite只会影响外部主操作流。
通过上⾯的例⼦,可能你会觉得Reactor Context好难⽤,⼀不⼩⼼就会掉到陷阱中。所以在使⽤Context的时候,⼀定要理解清楚Reactor Context的⾏为。在Spring 5.x中事务的实现就由Reactor的Context替换了ThreadLocal,理解Context是很有必要的。
Spring WebFlux中处理上下⽂
⾸先,理⼀下具体的思路。通过上⾯的学习我们知道Reactor可以处理上下⽂的信息传递,根据Reactor Context只对上游⽣效的特性,我们拦截请求在后置处理处做⼀次contextWrite或subscriberContext设置的上下⽂信息。这样就可以达到
我们的⽬的了。通过上⾯的分析,我们只⽤在Spring WebFlux中找到和Spring MVC中HandlerInterceptorAdapter类似的东西,在后置处理器中做contextWrite或subscriberContext操作就可以了。遗憾的是在Spring WebFlux中没有HandlerInterceptorAdapter,但是可以使⽤过滤器WebFilter。
@Component
@Slf4j
public class AuthFilter implements WebFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
return chain.filter(exchange).subscriberContext(ctx -> {
log.info("设置context内容。");
//注意这⾥⼀定要return ctx.put。put操作是产⽣⼀个新的context。如果是return ctx;会导致值没有设置进去
return  ctx.put("token", "xx");
});
}
}
按照上⾯的处理⽅式,在我们业务开发中就能取到上下⽂的信息了。
@PostMapping(name = "测试", value = "/save")
public Mono<Test> save(@RequestBody Mono<Test> testMono) {
return testService.add(testMono).log("", Level.INFO, true).flatMap(test -> {
return Mono.subscriberContext().map(ctx -> {
log.info("context:" + ("token"));
return test;
});
});
}
如果想设置多个值可以像下⾯这样操作。
@Component
public class WebFluxFilter implements WebFilter {
@Override
借款收据怎么写public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
return chain.filter(exchange)
.subscriberContext(ctx -> {
return ctx.put("token", "123123123123");
})
.subscriberContext(ctx -> {诗经里的女孩名字
贡菊return ctx.put("liu", "hah");
});
}
}
总结
魔蝎座女人
使⽤Spring WebFlux处理上下⽂,要先了解Reactor的Context处理逻辑。这样才能避免踩坑,毕竟上下⽂的信息错取引发的问题可不是⼩问题。
转载请注明出处
参考资料

本文发布于:2023-06-27 19:51:18,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/82/1053573.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:信息   处理   设置
相关文章
留言与评论(共有 0 条评论)
   
验证码:
推荐文章
排行榜
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图