千家信息网

怎么理解Java的回调与反应模式

发表于:2026-11-02 作者:千家信息网编辑
千家信息网最后更新 2026年11月02日,这篇文章主要介绍"怎么理解Java的回调与反应模式",在日常操作中,相信很多人在怎么理解Java的回调与反应模式问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答"怎么理解J
千家信息网最后更新 2026年11月02日怎么理解Java的回调与反应模式

这篇文章主要介绍"怎么理解Java的回调与反应模式",在日常操作中,相信很多人在怎么理解Java的回调与反应模式问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答"怎么理解Java的回调与反应模式"的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

1. 目标服务

客户端调用表示城市详细信息的目标服务有两个端口。当使用类型为--/cityids 的 URI 调用时,返回城市 id 列表,并且示例结果如下所示:

[    1,    2,    3,    4,    5,    6,    7]

一个端口返回给定其 ID 的城市的详细信息,例如,当使用 ID 为1--"/cities/1" 调用时:

{    "country": "USA",    "id": 1,    "name": "Portland",    "pop": 1600000}

客户端的责任是获取城市 ID 的列表,然后对于每个城市,根据 ID 获取城市的详细信息并将其组合到城市列表中。

2. 同步调用

我正在使用 Spring Framework 的 RestTemplate 进行远程调用。获取 cityId 列表的 Kotlin 函数如下所示:

private fun getCityIds(): List {    val cityIdsEntity: ResponseEntity> = restTemplate            .exchange("http://localhost:$localServerPort/cityids",                    HttpMethod.GET,                    null,                    object : ParameterizedTypeReference>() {})    return cityIdsEntity.body!!}

获取城市详情:

private fun getCityForId(id: String): City {    return restTemplate.getForObject("http://localhost:$localServerPort/cities/$id", City::class.java)!!}

鉴于这两个函数,它们很容易组合,以便于轻松返回城市列表 :

val cityIds: List = getCityIds()val cities: List = cityIds        .stream()        .map { cityId -> getCityForId(cityId) }        .collect(Collectors.toList())cities.forEach { city -> LOGGER.info(city.toString()) }

代码很容易理解;但是,涉及八个阻塞调用:

  1. 获取 7 个城市 ID 的列表,然后获取每个城市的详细信息

  2. 获取 7 个城市的详细信息

每一个调用都将在不同的线程上。

3. 非阻塞 IO 回调

我将使用 AsyncHttpClient 库来进行非阻塞 IO 调用。

进行远程调用时,AyncHttpClient 返回 ListenableFuture 类型。

val responseListenableFuture: ListenableFuture = asyncHttpClient                .prepareGet("http://localhost:$localServerPort/cityids")                .execute()

可以将回调附加到 ListenableFuture 以在可用时对响应进行操作。

responseListenableFuture.addListener(Runnable {    val response: Response = responseListenableFuture.get()    val responseBody: String = response.responseBody    val cityIds: List = objectMapper.readValue>(responseBody,            object : TypeReference>() {})    ....}

鉴于 cityIds 的列表,我想获得城市的详细信息,因此从响应中,我需要进行更多的远程调用并为每个调用附加回调以获取城市的详细信息:

val responseListenableFuture: ListenableFuture = asyncHttpClient        .prepareGet("http://localhost:$localServerPort/cityids")        .execute()responseListenableFuture.addListener(Runnable {    val response: Response = responseListenableFuture.get()    val responseBody: String = response.responseBody    val cityIds: List = objectMapper.readValue>(responseBody,            object : TypeReference>() {})    cityIds.stream().map { cityId ->        val cityListenableFuture = asyncHttpClient                .prepareGet("http://localhost:$localServerPort/cities/$cityId")                .execute()        cityListenableFuture.addListener(Runnable {            val cityDescResp = cityListenableFuture.get()            val cityDesc = cityDescResp.responseBody            val city = objectMapper.readValue(cityDesc, City::class.java)            LOGGER.info("Got city: $city")        }, executor)    }.collect(Collectors.toList())}, executor)

这是一段粗糙的代码;回调中又包含一组回调,很难推理和理解 - 因此它被称为"回调地狱"。

4. 在 Java CompletableFuture 中使用非阻塞 IO

通过将 Java 的 CompletableFuture 作为返回类型而不是 ListenableFuture 返回,可以稍微改进此代码。CompletableFuture 提供允许修改和返回类型的运算符。

例如,考虑获取城市 ID 列表的功能:

private fun getCityIds(): CompletableFuture> {    return asyncHttpClient            .prepareGet("http://localhost:$localServerPort/cityids")            .execute()            .toCompletableFuture()            .thenApply { response ->                val s = response.responseBody                val l: List = objectMapper.readValue(s, object : TypeReference>() {})                l            }}

在这里,我使用 thenApply 运算符将 CompletableFuture 转换为 CompletableFuture> 。

同样的,获取城市详情:

private fun getCityDetail(cityId: Long): CompletableFuture {    return asyncHttpClient.prepareGet("http://localhost:$localServerPort/cities/$cityId")            .execute()            .toCompletableFuture()            .thenApply { response ->                val s = response.responseBody                LOGGER.info("Got {}", s)                val city = objectMaper.readValue(s, City::class.java)                city            }}

这是基于回调的方法的改进。但是,在这个特定情况下,CompletableFuture 缺乏有用的运算符,例如,所有城市细节都需要放在一起:

val cityIdsFuture: CompletableFuture> = getCityIds()val citiesCompletableFuture: CompletableFuture> =        cityIdsFuture                .thenCompose { l ->                    val citiesCompletable: List> =                            l.stream()                                    .map { cityId ->                                        getCityDetail(cityId)                                    }.collect(toList())                    val citiesCompletableFutureOfList: CompletableFuture> = CompletableFuture.allOf(*citiesCompletable.toTypedArray())                                    .thenApply { _: Void? ->                                        citiesCompletable                                                .stream()                                                .map { it.join() }                                                .collect(toList())                                    }                    citiesCompletableFutureOfList                }

使用了一个名为 CompletableFuture.allOf 的运算符,它返回一个"Void"类型,并且必须强制返回所需类型的 CompletableFuture>。

5. 使用 Reactor 项目

Project Reactor 是 Reactive Streams 规范的实现。它有两种特殊类型可以返回 0/1 项的流和 0/n 项的流 - 前者是 Mono,后者是 Flux。

Project Reactor 提供了一组非常丰富的运算符,允许以各种方式转换数据流。首先考虑返回城市 ID 列表的函数:

private fun getCityIds(): Flux {    return webClient.get()            .uri("/cityids")            .exchange()            .flatMapMany { response ->                LOGGER.info("Received cities..")                response.bodyToFlux()            }}

我正在使用 Spring 优秀的 WebClient 库进行远程调用并获得 Project Reactor Mono 类型的响应,可以使用 flatMapMany 运算符将其修改为 Flux 类型。

根据城市 ID,沿着同样的路线获取城市的详情:

private fun getCityDetail(cityId: Long?): Mono {    return webClient.get()            .uri("/cities/{id}", cityId!!)            .exchange()            .flatMap { response ->                val city: Mono = response.bodyToMono()                LOGGER.info("Received city..")                city            }}

在这里,Project Reactor Mono 类型正在使用 flatMap 运算符转换为 Mono 类型。

以及从中获取 cityIds,这是 City 的代码:

val cityIdsFlux: Flux = getCityIds()val citiesFlux: Flux = cityIdsFlux        .flatMap { this.getCityDetail(it) }return citiesFlux

这非常具有表现力 - 对比基于回调的方法的混乱和基于 Reactive Streams 的方法的简单性。

到此,关于"怎么理解Java的回调与反应模式"的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注网站,小编会继续努力为大家带来更多实用的文章!

城市 类型 信息 运算符 运算 模式 反应 代码 方法 学习 阻塞 函数 更多 正在 详情 这是 两个 客户 目标 端口 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 南京欣网互联网络科技 互联网科技名片 互联网科技公司简介 科技和互联网公司 互联网电商科技 互联网与科技金融数据 有马互联网科技 互联网科技缩写 互联网前沿科技 互联网科技创新企业排名 贵州叶脉互联网科技有限公司所属行业 港股科技和互联网 互联网科技公司实习生 花神(南京)互联网科技有限公司 中国航天科技大学国际工业互联网 畅思互联网科技 西安小鹰速贷互联网科技有限公司 朱啸虎曾投资哪些互联网科技企业 世界互联网领先科技成果获奖 武汉亿视互联网科技有限公司 猪兼强互联网科技 互联网科技博主是什么意思 须眉科技是互联网公司吗 沐思想互联网科技有限公司 广州燕处互联网科技有限公司 山东大美朱楼互联网科技有限公司 广东鹅鹅鹅互联网科技有限公司 全球科技与互联网公司市值变化 科技和互联网行业的特点 北京瑞云互联网科技有限公司
0