最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring WebFlux使用函數(shù)式編程模型構(gòu)建異步非阻塞服務(wù)

 更新時間:2023年08月30日 15:42:01   作者:JavaEdge.  
這篇文章主要介紹了Spring WebFlux使用函數(shù)式編程模型構(gòu)建異步非阻塞服務(wù),重點(diǎn)介紹如何使用函數(shù)式編程模型創(chuàng)建響應(yīng)式 RESTful 服務(wù),這種編程模型與傳統(tǒng)的基于 Spring MVC 構(gòu)建 RESTful 服務(wù)的方法有較大差別,感興趣的朋友跟隨小編一起看看吧

1 前言

上文引入了 Spring 框架中專門用于構(gòu)建響應(yīng)式 Web 服務(wù)的 WebFlux 框架,同時我也給出了兩種創(chuàng)建 RESTful 風(fēng)格 HTTP 端點(diǎn)實(shí)現(xiàn)方法中的一種,即注解編程模型。

本文介紹另一種實(shí)現(xiàn)方法——如何使用函數(shù)式編程模型創(chuàng)建響應(yīng)式 RESTful 服務(wù),這種編程模型與傳統(tǒng)的基于 Spring MVC 構(gòu)建 RESTful 服務(wù)的方法有較大差別。

2 WebFlux 函數(shù)式編程模型

回顧Spring WebFlux系統(tǒng)架構(gòu)圖:

圖后半部分,Spring WebFlux 中,函數(shù)式編程模型的核心概念Router Functions,對標(biāo) Spring MVC 的 @Controller、@RequestMapping 等標(biāo)準(zhǔn)注解。而 Router Functions 則提供一套函數(shù)式風(fēng)格API,最重要的就是 Router、Handler 接口。可簡單將:

  • Router 對應(yīng)成 RequestMapping
  • Controller 對應(yīng)為 Handler

當(dāng)我發(fā)起一個遠(yuǎn)程調(diào)用,傳入的 HTTP 請求由 HandlerFunction 處理, HandlerFunction 本質(zhì)上是一個接收 ServerRequest 并返回 Mono 的函數(shù)。ServerRequest 和 ServerResponse 是一對不可變接口,用來提供對底層 HTTP 消息的友好訪問。

3 ServerRequest

代表請求對象,可訪問各種 HTTP 請求元素,包括請求方法、URI 和參數(shù),以及通過單獨(dú)的 ServerRequest.Headers 獲取 HTTP 請求頭信息。

ServerRequest 通過一系列 bodyToMono() 和 bodyToFlux() 方法提供對請求消息體進(jìn)行訪問的途徑。例如,如果我們希望將請求消息體提取為 Mono 類型的對象,可以使用如下方法。

Mono<String> string = request.bodyToMono(String.class);

而如果我們希望將請求消息體提取為 Flux 類型的對象,可以使用如下方法,其中 Order 是可以從請求消息體反序列化的實(shí)體類。

Flux<Order> order = request.bodyToFlux(Order.class);

上述的 bodyToMono() 和 bodyToFlux() 兩個方法實(shí)際上是通用的 ServerRequest.body(BodyExtractor) 工具方法的快捷方式,該方法如下所示。

<T> T body(BodyExtractor<T, ? super ServerHttpRequest> extractor);

BodyExtractor 是一種請求消息體的提取器,允許我們編寫自己的提取邏輯。請注意 BodyExtractor 提取的對象是一個 ServerHttpRequest 類型的實(shí)例,而這個 ServerHttpRequest 是非阻塞的,與之對應(yīng)的還有一個 ServerHttpResponse 對象。

響應(yīng)式 Web 操作的正是這組非阻塞的:

  • ServerHttpRequest
  • ServerHttpResponse

而不再是 Spring MVC 里的傳統(tǒng):

  • HttpServletRequest
  • HttpServletResponse

若不需要實(shí)現(xiàn)定制化的提取邏輯,可使用框架提供的常用的 BodyExtractors 實(shí)例。通過 BodyExtractors,上例可替換為。

Mono<String> string = 
	request.body(BodyExtractors.toMono(String.class);
Flux<Person> Order= 
	request.body(BodyExtractors.toFlux(Order.class);

ServerResponse

與ServerRequest 對應(yīng),ServerResponse 提供對 HTTP 響應(yīng)的訪問。由于不可變,因此可用構(gòu)建器創(chuàng)建一個新 ServerResponse。

構(gòu)建器允許設(shè)置響應(yīng)狀態(tài)、添加響應(yīng)標(biāo)題并提供響應(yīng)的具體內(nèi)容。如下示例演示如何通過 ok() 方法創(chuàng)建代表 200 狀態(tài)碼的響應(yīng),其中我將響應(yīng)體的類型設(shè)置為 JSON 格式,響應(yīng)具體內(nèi)容是 Mono 對象。

Mono<Order> order = …;
ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)
     .body(order);

通過 body() 方法來加載響應(yīng)內(nèi)容是構(gòu)建 ServerResponse 最常見的方法,這里我們將 Order 對象作為返回值。如果想要返回各種類型的對象,我們也可以使用 BodyInserters 工具類所提供的構(gòu)建方法,如常見的 fromObject() 和 fromPublisher() 方法等。以下示例代碼中,我們通過 fromObject() 方法直接返回一個 “Hello World”。

ServerResponse.ok().body(BodyInserters.fromObject("Hello World"));

上述方法的背后實(shí)際上是利用 BodyBuilder 接口中的一組 body() 方法,來構(gòu)建一個 ServerResponse 對象,典型的 body() 方法如下所示。

Mono<ServerResponse> body(BodyInserter<?, ? super ServerHttpResponse> inserter);

這里我們同樣看到了非阻塞式的 ServerHttpResponse 對象。這種 body() 方法比較常見的用法是返回新增和更新操作的結(jié)果,你在本講后續(xù)的內(nèi)容中將會看到這種使用方法。

HandlerFunction

將 ServerRequest 和 ServerResponse 組合在一起就可創(chuàng)建 HandlerFunction。

public interface HandlerFunction<T extends ServerResponse> {
    Mono<T> handle(ServerRequest request);
}

可通過實(shí)現(xiàn) HandlerFunction#handle() 創(chuàng)建定制化的請求響應(yīng)處理機(jī)制。

簡單的“Hello World”處理函數(shù)。

public class HelloWorldHandlerFunction implements 
	HandlerFunction<ServerResponse> {
        @Override
        public Mono<ServerResponse> handle(ServerRequest request) {
            return ServerResponse.ok().body(
	BodyInserters.fromObject("Hello World"));
        }
};

這里使用了前面介紹的 ServerResponse 所提供的 body() 方法返回一個 String 類型的消息體。

通常,針對某個領(lǐng)域?qū)嶓w都存在 CRUD 等常見的操作,所以需要編寫多個類似的處理函數(shù),煩瑣。推薦將多個處理函數(shù)分組到一個專門 Handler 類。

RouterFunction

已可通過 HandlerFunction 創(chuàng)建請求的處理邏輯,接下來需要把具體請求與這種處理邏輯關(guān)聯(lián)起來,RouterFunction 可以幫助我們實(shí)現(xiàn)這一目標(biāo)。RouterFunction 與傳統(tǒng) SpringMVC 中的 @RequestMapping 注解功能類似。

創(chuàng)建 RouterFunction 最常見做法是使用 route 方法,該方法通過使用請求謂詞和處理函數(shù)創(chuàng)建一個 ServerResponse 對象。

public static <T extends ServerResponse> RouterFunction<T> route(
            RequestPredicate predicate, HandlerFunction<T> handlerFunction) {
        return new DefaultRouterFunction<>(predicate, handlerFunction);
}

RouterFunction 的核心邏輯位于這里的 DefaultRouterFunction 類中,該類的 route() 方法如下所示。

public Mono<HandlerFunction<T>> route(ServerRequest request) {
            if (this.predicate.test(request)) {
                if (logger.isTraceEnabled()) {
                    String logPrefix = request.exchange().getLogPrefix();
                    logger.trace(logPrefix + String.format("Matched %s", this.predicate));
                }
                return Mono.just(this.handlerFunction);
            }
            else {
                return Mono.empty();
            }
}

該方法將傳入的 ServerRequest 路由到具體的處理函數(shù) HandlerFunction。如果請求與特定路由匹配,則返回處理函數(shù)的結(jié)果,否則就返回空Mono。

RequestPredicates 工具類提供了常用的謂詞,能夠?qū)崿F(xiàn)包括基于路徑、HTTP 方法、內(nèi)容類型等條件的自動匹配。

簡單 RouterFunction 示例,實(shí)現(xiàn)對 "/hello-world"請求路徑的自動路由:

RouterFunction<ServerResponse> helloWorldRoute =                RouterFunctions.route(RequestPredicates.path("/hello-world"),
            new HelloWorldHandlerFunction());

類似的,我們應(yīng)該把 RouterFunction 和各種 HandlerFunction 按照需求結(jié)合起來一起使用,常見的做法也是根據(jù)領(lǐng)域?qū)ο髞碓O(shè)計(jì)對應(yīng)的 RouterFunction。

路由機(jī)制的優(yōu)勢在于它的組合型。兩個路由功能可以組合成一個新的路由功能,并通過一定的評估方法路由到其中任何一個處理函數(shù)。如果第一個路由的謂詞不匹配,則第二個謂詞會被評估。請注意組合的路由器功能會按照順序進(jìn)行評估,因此在通用功能之前放置一些特定功能是一項(xiàng)最佳實(shí)踐。在 RouterFunction 中,同樣提供了對應(yīng)的組合方法來實(shí)現(xiàn)這一目標(biāo),請看下面的代碼。

default RouterFunction<T> and(RouterFunction<T> other) {
        return new RouterFunctions.SameComposedRouterFunction<>(this, other);
}
default RouterFunction<T> andRoute(RequestPredicate predicate, HandlerFunction<T> handlerFunction) {
        return and(RouterFunctions.route(predicate, handlerFunction));
}

我們可以通過調(diào)用上述兩個方法中的任意一個來組合兩個路由功能,其中后者相當(dāng)于 RouterFunction.and() 方法與 RouterFunctions.route() 方法的集成。以下代碼演示了 RouterFunctions 的組合特性。

RouterFunction<ServerResponse> personRoute =
        route(GET("/orders/{id}").and(accept(APPLICATION_JSON)), personHandler::getOrderById)
.andRoute(GET("/orders").and(accept(APPLICATION_JSON)), personHandler::getOrders)
.andRoute(POST("/orders").and(contentType(APPLICATION_JSON)), personHandler::createOrder);

RequestPredicates 工具類所提供的大多數(shù)謂詞也具備組合特性。例如, RequestPredicates.GET(String) 方法的實(shí)現(xiàn)如下所示。

public static RequestPredicate GET(String pattern) {
        return method(HttpMethod.GET).and(path(pattern));
}

可以看到,該方法是 RequestPredicates.method(HttpMethod.GET) 和 RequestPredicates.path(String) 的組合。我們可以通過調(diào)用 RequestPredicate.and(RequestPredicate) 方法或 RequestPredicate.or(RequestPredicate) 方法來構(gòu)建復(fù)雜的請求謂詞。

案例集成:ReactiveSpringCSS 中的 Web 服務(wù)

customer-service 分別需要訪問 account-service 和 order-service 服務(wù)中的 Web 服務(wù)。

已基于注解編程模型實(shí)現(xiàn)了 account-service 中的 AccountController。今天演示 order-service 中 Web 服務(wù)實(shí)現(xiàn)過程。

基于函數(shù)式編程模型,在 order-service 中,編寫 OrderHandler 專門實(shí)現(xiàn)根據(jù) OrderNumber 獲取 Order 領(lǐng)域?qū)嶓w的處理函數(shù)

@Configuration
public class OrderHandler {
    @Autowired
    private OrderService orderService;
    public Mono<ServerResponse> getOrderByOrderNumber(ServerRequest request) {
        String orderNumber = request.pathVariable("orderNumber");
        return ServerResponse.ok().body(this.orderService.getOrderByOrderNumber(orderNumber), Order.class);
    }
}

上述代碼示例中,我們創(chuàng)建了一個 OrderHandler 類,然后注入 OrderService 并實(shí)現(xiàn)了一個 getOrderByOrderNumber() 處理函數(shù)。

現(xiàn)在我們已經(jīng)具備了 OrderHandler,就可以創(chuàng)建對應(yīng)的 OrderRouter 了,示例代碼如下。

@Configuration
public class OrderRouter {
    @Bean
    public RouterFunction<ServerResponse> routeOrder(OrderHandler orderHandler) {
        return RouterFunctions.route(
                RequestPredicates.GET("/orders/{orderNumber}")
                    .and(RequestPredicates.accept(MediaType.APPLICATION_JSON)),
                    orderHandler::getOrderByOrderNumber);
    }
}

在這個示例中,我們通過訪問“/orders/{orderNumber}”端點(diǎn)就會自動觸發(fā) orderHandler 中的 getOrderByOrderNumber() 方法并返回相應(yīng)的 ServerResponse。

接下來,假設(shè)我們已經(jīng)分別通過遠(yuǎn)程調(diào)用獲取了目標(biāo) Account 對象和 Order 對象,那么 generateCustomerTicket 方法的執(zhí)行流程就可以明確了?;陧憫?yīng)式編程的實(shí)現(xiàn)方法,我們可以得到如下所示的示例代碼。

public Mono<CustomerTicket> generateCustomerTicket(String accountId, String orderNumber) {
        // 創(chuàng)建 CustomerTicket 對象
        CustomerTicket customerTicket = new CustomerTicket();
        customerTicket.setId("C_" + UUID.randomUUID().toString());
        // 從遠(yuǎn)程 account-service 獲取 Account 對象
        Mono<AccountMapper> accountMapper = getRemoteAccountByAccountId(accountId);
        // 從遠(yuǎn)程 order-service 中獲取 Order 對象
        Mono<OrderMapper> orderMapper = getRemoteOrderByOrderNumber(orderNumber);
        Mono<CustomerTicket> monoCustomerTicket =
                Mono.zip(accountMapper, orderMapper).flatMap(tuple -> {
            AccountMapper account = tuple.getT1();
            OrderMapper order = tuple.getT2();
            if(account == null || order == null) {
                return Mono.just(customerTicket);
            }
            // 設(shè)置 CustomerTicket 對象屬性
            customerTicket.setAccountId(account.getId());
            customerTicket.setOrderNumber(order.getOrderNumber());
            customerTicket.setCreateTime(new Date());
            customerTicket.setDescription("TestCustomerTicket");
            return Mono.just(customerTicket);
        });
        // 保存 CustomerTicket 對象并返回
        return monoCustomerTicket.flatMap(customerTicketRepository::save);
}

顯然,這里的 getRemoteAccountById 和 getRemoteOrderByOrderNumber 方法都涉及了非阻塞式的遠(yuǎn)程 Web 服務(wù)的調(diào)用,這一過程我們將放在下一講中詳細(xì)介紹。

請注意,到這里時使用了 Reactor 框架中的 zip 操作符,將 accountMapper 流中的元素與 orderMapper 流中的元素按照一對一的方式進(jìn)行合并,合并之后得到一個 Tuple2 對象。然后,我們再分別從這個 Tuple2 對象中獲取 AccountMapper 和 OrderMapper 對象,并將它們的屬性填充到所生成的 CustomerTicket 對象中。最后,我們通過 flatMap 操作符調(diào)用了 customerTicketRepository 的 save 方法完成了數(shù)據(jù)的持久化。這是 zip 和 flatMap 這兩個操作符非常經(jīng)典的一種應(yīng)用場景,你需要熟練掌握。

總結(jié)

好了,那么本講內(nèi)容就說到這。延續(xù)上一講,我們接著討論了 Spring WebFlux 的使用方法,并給出了基于函數(shù)式編程模型的 RESTful 端點(diǎn)創(chuàng)建方法。在這種開發(fā)模型中,重點(diǎn)把握:

  • ServerRequest
  • ServerResponse
  • HandlerFunction
  • RouterFunction

核心對象的使用方法。

FAQ

WebFlux 函數(shù)式編程模型中包含哪些核心編程對象嗎?

現(xiàn)在,我們已經(jīng)通過 WebFlux 構(gòu)建了響應(yīng)式 Web 服務(wù),下一步就是如何來消費(fèi)它們了。Spring 也專門提供了一個非阻塞式的 WebClient 工具類來完成這一目標(biāo),下一講我就來和你系統(tǒng)地討論這個工具類的使用方法,到時見。

到此這篇關(guān)于Spring WebFlux使用函數(shù)式編程模型構(gòu)建異步非阻塞服務(wù)的文章就介紹到這了,更多相關(guān)Spring WebFlux構(gòu)建異步非阻塞服務(wù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Scala小程序詳解及實(shí)例代碼

    Scala小程序詳解及實(shí)例代碼

    這篇文章主要介紹了Scala 第一個Scala小程序詳解的相關(guān)資料,需要的朋友可以參考下
    2017-01-01
  • Spring Boot MyBatis 操作數(shù)據(jù)庫的實(shí)戰(zhàn)指南

    Spring Boot MyBatis 操作數(shù)據(jù)庫的實(shí)戰(zhàn)指南

    本文詳細(xì)介紹了SpringBoot環(huán)境下MyBatis的集成與應(yīng)用,從傳統(tǒng)JDBC的局限性出發(fā),展示了MyBatis的日志配置、CRUD操作、自增主鍵返回、多表查詢等功能,感興趣的朋友跟隨小編一起看看吧
    2026-05-05
  • 關(guān)于微服務(wù)使用Dubbo設(shè)置的端口和server.port的區(qū)別

    關(guān)于微服務(wù)使用Dubbo設(shè)置的端口和server.port的區(qū)別

    這篇文章主要介紹了關(guān)于微服務(wù)使用Dubbo設(shè)置的端口和server.port的區(qū)別,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • SpringCloud之Config配置中心與Redis分布式鎖詳解

    SpringCloud之Config配置中心與Redis分布式鎖詳解

    這篇文章主要給大家介紹了SpringCloud Alibaba中Config配置中心,Redis分布式鎖,文中有詳細(xì)的代碼示例供大家參考,需要的朋友可以參考閱讀
    2023-05-05
  • 關(guān)于Java中HashCode方法的深入理解

    關(guān)于Java中HashCode方法的深入理解

    這篇文章主要給大家介紹了關(guān)于Java中HashCode方法的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用Java具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-08-08
  • Java判斷ip是否為IPV4或IPV6地址的多種方式

    Java判斷ip是否為IPV4或IPV6地址的多種方式

    本文主要介紹了Java判斷ip是否為IPV4或IPV6地址的多種方式,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-03-03
  • Java全面細(xì)致講解類與對象

    Java全面細(xì)致講解類與對象

    類和對象是兩種以計(jì)算機(jī)為載體的計(jì)算機(jī)語言的合稱。對象是對客觀事物的抽象,類是對對象的抽象。類是一種抽象的數(shù)據(jù)類型;變量就是可以變化的量,存儲在內(nèi)存中—個可以擁有在某個范圍內(nèi)的可變存儲區(qū)域
    2022-05-05
  • MyBatis #{}和${} |與數(shù)據(jù)庫連接池使用詳解

    MyBatis #{}和${} |與數(shù)據(jù)庫連接池使用詳解

    本文將為大家說說關(guān)于 #{} 和 ${},這個是 MyBatis 在面試中最常問的面試題,以及數(shù)據(jù)庫連接池相關(guān)的知識,感興趣的朋友跟隨小編一起看看吧
    2024-01-01
  • 使用mongoTemplate實(shí)現(xiàn)多條件加分組查詢方式

    使用mongoTemplate實(shí)現(xiàn)多條件加分組查詢方式

    這篇文章主要介紹了使用mongoTemplate實(shí)現(xiàn)多條件加分組查詢方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • Flink時間和窗口邏輯處理源碼分析

    Flink時間和窗口邏輯處理源碼分析

    這篇文章主要為大家介紹了Flink時間和窗口邏輯處理源碼分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12

最新評論

潼关县| 明溪县| 遂宁市| 陵川县| 随州市| 比如县| 金川县| 四会市| 康定县| 衢州市| 原平市| 石城县| 星子县| 航空| 丹寨县| 乐业县| 新绛县| 郁南县| 孝昌县| 云龙县| 平远县| 皮山县| 东兴市| 根河市| 永登县| 大石桥市| 昔阳县| 尚志市| 驻马店市| 综艺| 金塔县| 邻水| 顺昌县| 永和县| 红河县| 嘉禾县| 商水县| 德钦县| 全南县| 青铜峡市| 涡阳县|