1. 程式人生 > >Spring5.0響應式編程入門

Spring5.0響應式編程入門

JD 字符 增加 配置服務 控制 數據類型 pin 開發人員 簡單

引言
? 響應式編程是一種面向數據流和變化傳播的編程範式。使用它可以在編程語言中很方便地表達靜態或動態的數據流,而相關的計算模型會自動將變化的值通過數據流進行傳播。我們可以使用聲明的方式構建應用程序的能力,形成更加敏感和有彈性的應用,所以Spring 5在其核心框架中增加了反應系統,已經開始向聲明式編程的範式轉變。

響應式編程的優勢

  • 提高了代碼的可讀性,因此開發人員只需要關註定義業務邏輯。

  • 在高並發環境中,可以自然的處理消息。

  • 可以控制生產者和消費者之間的流量,避免內存不足。

  • 對於一個或多個線程,IO綁定任務可以通過異步和非阻塞方式執行,而且不阻塞當前線程。

  • 可以有效的管理多個連接系統之間的通信。

應用場景

  • 大量的交易處理服務,如銀行部門。

  • 大型在線購物應用程序的通知服務,如亞馬遜。

  • 股票價格同時變動的股票交易業務。

Spring 5.0前瞻
作為Java中的首個響應式Web框架,Spring 5.0最大的亮點莫過於提供了完整的端到端響應式編程的支持。
技術分享圖片
如上圖所示左側是傳統的基於Servlet的Spring Web MVC框架,右側是spring 5.0新引入的基於Reactive Streams的Spring WebFlux框架,從上往下依次是:Router Functions,WebFlux,Reactive Streams三個新組件,其中:

  • Router Functions: 對標@Controller,@RequestMapping等標準的Spring MVC註解,提供一套函數式風格的API,用於創建Router,Handler和Filter。

  • WebFlux: 核心組件,協調上下遊各個組件提供響應式編程支持。

  • Reactive Streams: 一種支持背壓(Backpressure)的異步數據流處理標準,主流實現有RxJava和Reactor,Spring WebFlux默認集成的是Reactor。

示例代碼
1.創建項目
spring響應式開發,需要結合spring boot來完成,所以使用idea創建springboot項目,選擇Spring Initializr選項,jdk選擇1.8以上。
技術分享圖片
選擇下一步,設置groupId和artifactId。
技術分享圖片
選擇下一步,選擇web選項中的Reactive web選項,表明要創建響應式項目,註意Spring Boot的版本為2.0.2。

技術分享圖片
選擇下一步,設置項目的目錄,點擊finish創建項目。
技術分享圖片
2.設置服務端口
為了避免端口沖突,我們可以在項目的application.properties文件中配置服務端口。

server.port=9002
3.相關依賴包
打開項目的pom.xml文件,會發現以下幾個依賴包

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-webflux</artifactId>
    </dependency>
?
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>io.projectreactor</groupId>
        <artifactId>reactor-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

其中:

  • spring-boot-starter-webflux:webflux依賴包,是響應式開發的核心依賴包,其中包含了spring-boot-starter-reactor-netty 、spring 5 webflux 包,默認是通過netty啟動的。

  • spring-boot-starter-test:springboot的單元測試工具庫。

  • reactor-test:Spring 5提供的官方針對RP框架測試工具庫。

小結
? spring響應式開發需要結合spring boot來完成,同時需要引入spring-boot-starter-webflux和reactor-test兩個依賴包來支持響應式開發。

4.使用webflux創建web應用
webflux的使用有兩種方式,基於註解和函數式編程。這裏使用函數式編程,具體操作如下:

4.1.創建實體類

public class Good {
?
    private int id;
    private String name;
    private String price;
?
    public Good(int id,String name,String price){
        this.id=id;
        this.name=name;
        this.price=price;
    }
?
    public int getId() {
        return id;
    }
?
    public void setId(int id) {
        this.id = id;
    }
?
    public String getName() {
        return name;
    }
?
    public void setName(String name) {
        this.name = name;
    }
?
    public String getPrice() {
        return price;
    }
?
    public void setPrice(String price) {
        this.price = price;
    }
?
    @Override
    public String toString() {
        return "Good{" +
                "id=" + id +
                ", name=‘" + name + ‘\‘‘ +
                ", price=‘" + price + ‘\‘‘ +
                ‘}‘;
    }
}

分析: 實體類沒有什麽特殊的操作,原來怎麽操作現在還是怎麽操作。

4.2.創建GoodGenerator

@Configuration
public class GoodGenerator {
?
    public Flux<Good> findGoods(){
        List<Good> goods = new ArrayList<>();
        goods.add(new Good(1,"小米","2000"));
        goods.add(new Good(2,"華為","4000"));
        goods.add(new Good(3,"蘋果","8000"));
        return Flux.fromIterable(goods);
    }
}

分析: 這裏的方法返回的是Flux類型的數據,Flux是RP中最基礎的數據類型之一,對應的是多值數據的返回操作,在RP中還有Mono數據類型,也是最基礎的數據類型之一,對應單值數據的返回操作。

註意:這裏的GoodGenerator類要加上@Configuration註解。

4.3.創建GoodHandler

@Component
@Configuration
public class GoodHandler {
?
    private final Flux<Good> goods;
?
    public GoodHandler(GoodGenerator goodGenerator) {
        this.goods = goodGenerator.findGoods();
    }
?
    public Mono<ServerResponse> hello(ServerRequest request) {
?
        return ok().contentType(TEXT_PLAIN)
                .body(BodyInserters.fromObject("Hello Spring!"));
    }
?
    public Mono<ServerResponse> echo(ServerRequest request) {
        return ok().contentType(APPLICATION_STREAM_JSON)
                .body(this.goods,Good.class);
    }
}

分析: Handler主要用來處理請求操作,並將Mono<ServerResponse>返回,Mono<ServerResponse>中會封裝響應數據,響應數據如果是字符串可以使用:


ok().contentType(TEXT_PLAIN).body(BodyInserters.fromObject("Hello Spring!"));

操作,如果是集合數據可以使用:


ok().contentType(APPLICATION_STREAM_JSON).body(this.goods,Good.class)

操作。

4.4.創建GoodRouter


@Configuration
public class GoodRouter {
    @Bean
    public RouterFunction<ServerResponse> route(GoodHandler goodHandler) {
?
      return RouterFunctions
              .route(RequestPredicates.GET("/good")
                      .and(RequestPredicates.accept(MediaType.TEXT_PLAIN)),goodHandler::hello)
              .andRoute(RequestPredicates.GET("/goods")
                      .and(RequestPredicates.accept(MediaType.APPLICATION_STREAM_JSON)),goodHandler::echo);
    }
?
}

分析: GoodRouter主要用來設置請求路徑和轉化HTTP請求,可以使用route()方法和andRoute方法設置多個請求路徑和轉化操作。

小結
? HTTP請求會由GoodRouter轉發給對應的Handler,Handler處理請求,並返回Mono<ServerResponse>,這裏的Router類似@RequestMapping,Handler類似Controller

4.4.運行測試
? 實體類、GoodGenerator、GoodHandler、GoodRouter都已經創建完成了,我們可以運行項目打開瀏覽器進行測試.

瀏覽器輸入http://localhost:9002/good,即可獲取到"Hello Spring!"文本信息
技術分享圖片

瀏覽器輸入http://localhost:9002/goods,即可獲取到集合信息

技術分享圖片
到目前為止,一個簡單的webflux示例已經完成。

4.5.單元測試
在項目中我們也可以使用使用一個Spring 5新引入的測試工具類,WebTestClient,專門用於測試RP應用,具體代碼如下:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class Spring5demoApplicationTests {
?
    @Autowired
    private WebTestClient webTestClient;
?
    @Test
    public void helloTest() {
      String s=  webTestClient
                .get().uri("/good")
                .accept(MediaType.TEXT_PLAIN).exchange()
                .expectStatus().isOk().returnResult(String.class)
                .getResponseBody().blockFirst();
?
        System.out.println(s);
    }
?
    @Test
    public void findGoodsTest(){
        webTestClient.get().uri("/goods")
                .accept(MediaType.APPLICATION_STREAM_JSON)
                .exchange().expectStatus().isOk()
                .expectHeader().contentType(MediaType.APPLICATION_STREAM_JSON)
                .returnResult(Good.class)
                .getResponseBody().collectList();
    }
}

創建WebTestClient實例時可以看到,編寫RP應用的單元測試,同樣也是數據不落地的流式風格

總結
? 到此,spring 5.0的響應式編程就給大家介紹到這裏,這裏只是簡單進行了一個響應式入門操作,但是也能夠體現出響應式編程的特點。當然spring 5.0響應式編程也不是完美的,它在故障診斷、依賴庫集成、數據存儲以及Spring Security安全權限框架支持等方面還是有局限性的。?

Spring5.0響應式編程入門