Reactive编程:如何优化Mono嵌套语法并修复令牌拉黑逻辑异常执行问题
问题原因
你改写后的代码存在两个核心问题:
then运算符的参数是立即求值的:你写.then(blacklistTokens(username, refreshToken, accessToken))时,blacklistTokens方法会在流组装阶段就直接执行,不会等上游校验逻辑完成,这是哪怕抛出异常拉黑操作依然会执行的核心原因。isBlacklistedToken的实现不符合响应式规范:你用Mono.just直接包裹repository调用,若repository方法是阻塞的,该调用会在方法执行时同步触发,不会延迟到流订阅阶段;若repository本身是响应式实现,额外套Mono.just属于多余操作,也会导致逻辑提前执行。
优化后的代码写法
你可以用filter + switchIfEmpty的写法简化嵌套逻辑,同时用Mono.defer延迟构造拉黑操作的Publisher,确保只有校验全部通过后才会执行:
return isBlacklistedToken(refreshToken, Boolean.TRUE) // 校验refresh token未被拉黑,否则抛出异常 .filter(isBlacklisted -> !isBlacklisted) .switchIfEmpty(Mono.error(new UnauthorizedException(String.format("The user %s has already logged out.", username)))) // 校验access token .then(isBlacklistedToken(accessToken, Boolean.FALSE)) .filter(isBlacklisted -> !isBlacklisted) .switchIfEmpty(Mono.error(new UnauthorizedException(String.format("The user %s has already logged out.", username)))) // 延迟构造拉黑操作,仅当前面所有校验通过后才执行 .then(Mono.defer(() -> blacklistTokens(username, refreshToken, accessToken)));
如果你偏好和原有逻辑对齐的flatMap链式写法,也可以用下面的版本,完全避免嵌套:
return isBlacklistedToken(refreshToken, Boolean.TRUE) .flatMap(isRefreshBlacklisted -> { if (isRefreshBlacklisted) { return Mono.error(new UnauthorizedException(String.format("The user %s has already logged out.", username))); } return isBlacklistedToken(accessToken, Boolean.FALSE); }) .flatMap(isAccessBlacklisted -> { if (isAccessBlacklisted) { return Mono.error(new UnauthorizedException(String.format("The user %s has already logged out.", username))); } return blacklistTokens(username, refreshToken, accessToken); });
补充修正响应式方法实现
你需要同步修正blacklistService.isBlacklisted的实现,避免逻辑提前执行:
- 若
blacklistRepository是阻塞实现(如JPA):
public Mono<Boolean> isBlacklisted(final String token, final Boolean isRefresh) { return Mono.fromCallable(() -> this.blacklistRepository.existsBlacklistByTokenAndIsRefresh(token, isRefresh)) .subscribeOn(Schedulers.boundedElastic()); }
- 若
blacklistRepository是响应式实现(如R2DBC):
public Mono<Boolean> isBlacklisted(final String token, final Boolean isRefresh) { // 直接返回repository的返回值,无需额外包裹Mono.just return this.blacklistRepository.existsBlacklistByTokenAndIsRefresh(token, isRefresh); }
内容的提问来源于stack exchange,提问作者Mauricio
相关产品推荐
相关产品推荐

