Spring Boot运行时无法获取所有请求作用域Bean,异步请求挂起问题
问题分析与解决方案
核心问题根源
你用@RequestScope的思路完全错了——请求作用域的Bean是和单个HTTP请求上下文绑定的:
- 每个
/future请求都会创建专属的FutureManager实例,这个实例只存在于当前请求的上下文(RequestAttributes)中 - 当你调用
/complete时,这个请求有自己独立的上下文,通过ApplicationContext.getBeansOfType只能拿到当前/complete请求自己生成的FutureManager实例,根本访问不到前两个/future请求的实例。日志里的第三个实例就是/complete请求自己创建的,和前两个毫无关系。
正确实现方案
需要用一个全局的线程安全容器来管理所有待完成的CompletableFuture,而不是依赖请求作用域。以下是修正后的代码:
1. 重构FutureManager为单例服务
@Service class FutureManager { // 使用线程安全的集合存储所有待完成的Future private val pendingFutures = CopyOnWriteArrayList<CompletableFuture<Boolean>>() fun complete(value: Boolean) { // 完成所有待处理的Future pendingFutures.forEach { it.complete(value) } // 清理已完成的Future,避免内存泄漏 pendingFutures.removeIf { it.isDone } } fun get(): Boolean { val future = CompletableFuture<Boolean>() pendingFutures.add(future) // 注册回调:Future完成(无论成功/失败)时自动从集合移除 future.whenComplete { _, _ -> pendingFutures.remove(future) } return future.get() } }
2. 简化控制器实现
不需要再通过ApplicationContext查找Bean,直接注入单例的FutureManager即可:
@RestController class FutureController( private val manager: FutureManager ) { @GetMapping("/future") fun getFuture(): Boolean { println("New client waiting for value") return manager.get() } } @RestController class CompleteFutureController( private val manager: FutureManager ) { @GetMapping("/complete") fun completeFuture(@RequestParam value: Boolean) { manager.complete(value) println("Completed all pending futures with value: $value") } }
关键细节说明
CopyOnWriteArrayList保证了多线程下遍历、添加、移除操作的线程安全,适合这种读多写少的场景- 加入
whenComplete回调是为了防止内存泄漏:如果某个/future请求超时或异常结束,对应的Future会自动从集合中移除 - 如果需要支持按不同主题分发消息,可以把集合改成
ConcurrentHashMap<String, List<CompletableFuture<Boolean>>>,用主题作为key实现分组通知
内容的提问来源于stack exchange,提问作者J.Kennsy
相关产品推荐
相关产品推荐

