spring5的新特性


1.背景

2.依賴環境的變化

整個 Spring5 框架的代碼基於 Java8,運行時兼容 JDK9,許多不建議使用的類和方 法在代碼庫中刪除

3.自帶了通用的日志封裝

3.1.日志的簡單使用

Spring 5.x框架自帶了通用的日志封裝

(1)Spring5 已經移除 Log4jConfigListener,官方建議使用 Log4j2

(2)Spring5 框架整合 Log4j2

使用步驟:

步驟一: 引入 jar 包

步驟二: 創建 log4j2.xml 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!--日志級別以及優先級排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE >
ALL -->
<!--Configuration 后面的 status 用於設置 log4j2 自身內部的信息輸出,可以不設置,
當設置成 trace 時,可以看到 log4j2 內部各種詳細輸出-->
<configuration status="INFO">
    <!--先定義所有的 appender-->
    <appenders>
        <!--輸出日志信息到控制台-->
        <console name="Console" target="SYSTEM_OUT">
            <!--
       日志輸出格式:
           %d表示日期時間,
           %thread表示線程名,
           %-5level:級別從左顯示5個字符寬度
           %logger{50} 表示logger名字最長50個字符,否則按照句點分割。
           %msg:日志消息,
           %n是換行符
       -->
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%-5level] %logger{36} - %msg%n"/>
        </console>
    </appenders>
    <!--然后定義 logger,只有定義 logger 並引入的 appender,appender 才會生效-->
    <!--root:用於指定項目的根日志,如果沒有單獨指定 Logger,則會使用 root 作為
   默認的日志輸出-->
    <loggers>
        <root level="info">
            <appender-ref ref="Console"/>
        </root>
    </loggers>
</configuration>
View Code

步驟三:測試

package com.ldp.test;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author 姿勢帝-博客園
 * @address https://www.cnblogs.com/newAndHui/
 * @WeChat 851298348
 * @create 01/14 7:07
 * @description
 */
public class TestLog4j2 {
    private static final Logger log = LoggerFactory.getLogger(TestLog4j2.class);

    /**
     * log4j2測試
     * 日志級別以及優先級排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE >
     *
     * @param args
     */
    public static void main(String[] args) {
        log.error("error......");
        log.warn("warn......");
        log.info("info......");
        log.debug("debug......");
        log.trace("trace.........");
    }
}
View Code

3.2.spring中的測試

1.原來在spring中的測試

package com.ldp.test;

import com.ldp.model.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/**
 * @author 姿勢帝-博客園
 * @address https://www.cnblogs.com/newAndHui/
 * @WeChat 851298348
 * @create 01/14 7:24
 * @description
 */
// junit4的測試方式
@RunWith(SpringJUnit4ClassRunner.class)
// 加載配置文件
@ContextConfiguration("classpath:bean1.xml")
public class TestJunit4 {
    @Autowired
    private User user;

    @Test
    public void test01() {
        System.out.println(user);
    }
}
View Code

2.spring5中的測試

package com.ldp.test;

import com.ldp.model.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;

/**
 * @author 姿勢帝-博客園
 * @address https://www.cnblogs.com/newAndHui/
 * @WeChat 851298348
 * @create 01/14 7:24
 * @description
 */

/**
 * junit4的測試方式
 *
 * @RunWith(SpringJUnit4ClassRunner.class)
 * @ContextConfiguration("classpath:bean1.xml")
 */
// spring5的測試
@SpringJUnitConfig(locations = "classpath:bean1.xml")
public class TestJunit5 {
    @Autowired
    private User user;

    /**
     * 注意spring5中的這個@Test注解是 import org.junit.jupiter.api.Test;
     * 而原來使用的的這個@Test注解是 import org.junit.Test;
     */
    @Test
    public void test01() {
        System.out.println(user);
    }
}
View Code

二者在使用上的區別

 個人覺得在常用的功能上沒有什么大的區別,只是Junit5寫起來簡潔一點

4.Spring5 框架核心容器支持@Nullable 注解

@Nullable 注解可以使用在方法上面,屬性上面,參數上面,表示方法返回可以為空,屬性值可以為空,參數值可以為空

5.Spring5 核心容器支持函數式風格 GenericApplicationContext

 /**
     * 函數創建對象演示
     *
     * @param args
     */
    public static void main(String[] args) {
        //1 創建 GenericApplicationContext 對象
        GenericApplicationContext context = new GenericApplicationContext();
        //2 調用 context 的方法對象注冊
        context.refresh();
        context.registerBean("user1", User.class, () -> new User());
        //3 獲取在 spring 注冊的對象
        // User user = (User)context.getBean("com.atguigu.spring5.test.User");
        User user = (User) context.getBean("user1");
        System.out.println(user);
    }
View Code

6.Spring5 框架新功能(Webflux)

6.1.SpringWebflux 介紹

(1)webFlux是 Spring5 添加的新模塊,用於 web 的開發,功能和 SpringMVC 類似的,Webflux 使用 當前一種比較流程響應式編程出現的框架。

在spring5的jar包中和架構圖中我們都可以看見

(2)使用傳統 web 框架,比如 SpringMVC,這些基於 Servlet 容器,Webflux 是一種異步非阻塞的框架,

  異步非阻塞的框架在 Servlet3.X以后才支持,核心是基於 Reactor 的API 實現的。

(3)異步與同步,阻塞與非阻塞的理解

  這里做一個簡單的通俗的解釋:

  1.異步和同步針對調用者,調用者發送請求,如果等着對方回應之后才去做其他事情就是同步,如果發送請求之后不等着對方回應就去做其他事情就是異步

  2.阻塞和非阻塞針對被調用者,被調用者接受到請求之后,做完請求任務之后才給出反饋就是阻塞,接受到請求之后馬上給出反饋然后再去做事情就是非阻塞

  如果想深入理解並實踐應用異步同步阻塞非阻塞等技術,可能會涉及到網絡編程、socket、BIO、NIO、AIO、Netty等技術,大家可以學習之前講的《網絡編程系列課程》

(4)Webflux 特點:

特性一、 異步非阻塞
SpringMVC是同步阻塞的IO模型,資源浪費相對來說比較嚴重,當我們在處理一個比較耗時的任務時,
例如:上傳一個比較大的文件,首先,服務器的線程一直在等待接收文件,在這期間它就像個傻子一樣等在那兒(放學別走),什么都干不了,好不容易等到文件來了並且接收完畢,
我們又要將文件寫入磁盤,在這寫入的過程中,這根線程又要等到文件寫完才能去干其它的事情。這一前一后的等待,浪費了大量的資源。
而Spring WebFlux就是來解決這個問題的,
Spring WebFlux可以做到異步非阻塞。還是上面那上傳文件的例子,
Spring WebFlux是這樣做的:線程發現文件還沒准備好,就先去做其它事情,當文件准備好之后,通知這根線程來處理,
當接收完畢寫入磁盤的時候(根據具體情況選擇是否做異步非阻塞),寫入完畢后通知這根線程再來處理(異步非阻塞情況下)。
這個設計相對於SpringMVC而言,可以大大節省系統資源。 特性二、 響應式(reactive)函數編程 Spring5 框架基於 java8,Webflux 使用 Java8 函數式編程方式實現路由請求,如果你覺得java8的lambda寫起來很爽,那么,你會再次喜歡上Spring WebFlux, 因為它支持函數式編程,得益於對於reactive-stream的支持(通過reactor框架來實現的)。 特性三、 不再拘束於Servlet容器 以前,我們的應用都運行於Servlet容器之中,例如我們大家最為熟悉的Tomcat, Jetty...等等。
而現在Spring WebFlux不僅能運行於傳統的Servlet容器中(前提是容器要支持Servlet3.1,因為非阻塞IO是使用了Servlet3.1的特性),還能運行在支持NIO的Netty和Undertow中。

(5)Webflux與SpringMVC的區別

  區別一: 兩個框架都可以使用注解方式,都可以運行在 Tomet 等容器中
  區別二: SpringMVC 采用命令式編程,Webflux 采用異步響應式編程

  

   具體的可以看后面的案例演示,可能會更好理解!

  (6)總結(面試的時候很重要)

  特點
  1.webflux是一個異步非阻塞的Web框架,它能夠充分利用多核CPU的硬件資源去處理大量的並發請求

  2.內部使用的是響應式編程,以Reactor庫為基礎,基於異步和事件驅動,可以讓我們在不擴充硬件資源的前提下,提升系統的吞吐量和伸縮性。

  3.不能使接口的響應時間縮短,它僅僅能夠提升吞吐量和伸縮性。

  應用場景
  1.特別適合在IO密集型的服務中,比如微服務網關。

  2.IO 密集型包括:磁盤IO密集型, 網絡IO密集型,

  微服務網關就屬於網絡 IO 密集型,使用異步非阻塞式編程模型,能夠顯著地提升網關對下游服務轉發的吞吐量。

  選WebFlux還是Spring MVC
  1.WebFlux不是 Spring MVC的替代方案!雖然 WebFlux 也可以被運行在 Servlet 容器上(需是 Servlet 3.1+ 以上的容器),

  但是 WebFlux 主要還是應用在異步非阻塞編程模型,而 Spring MVC 是同步阻塞的,

  如果你目前在 Spring MVC 框架中大量使用非同步方案,那么,WebFlux 才是你想要的,否則,使用 Spring MVC 才是你的首選。

  2.在微服務架構中,Spring MVC 和 WebFlux 可以混合使用,比如已經提到的,對於那些 IO 密集型服務(如網關),我們就可以使用 WebFlux 來實現。

6.2.響應式編程(Java 實現)

package com.ldp.testWebflux;


import java.util.Observable;

/**
 * @author 姿勢帝-博客園
 * @address https://www.cnblogs.com/newAndHui/
 * @WeChat 851298348
 * @create 01/15 7:14
 * @description <p>
 * 什么是響應式編程
 * 響應式編程是一種面向數據流和變化傳播的編程范式。這意味着可以在編程語言中很方便
 * 地表達靜態或動態的數據流,而相關的計算模型會自動將變化的值通過數據流進行傳播。
 * 電子表格程序就是響應式編程的一個例子。單元格可以包含字面值或類似"=B1+C1"的公
 * 式,而包含公式的單元格的值會依據其他單元格的值的變化而變化。
 * </p>
 */
public class Test01Observer extends Observable {
    /**
     * @param args
     */
    public static void main(String[] args) {
        Test01Observer observer = new Test01Observer();
        // 添加觀察者
        observer.addObserver((o, arg) -> {
            System.out.println("通知數據變化---1");
        });
        observer.addObserver((o, arg) -> {
            System.out.println("通知數據變化---2");
        });
        observer.addObserver((o, arg) -> {
            System.out.println("通知數據變化---3");
        });
        // 數據變化
        observer.setChanged();
        // 通知
        observer.notifyObservers();
        System.out.println("代碼執行完成.....4");
    }
}
View Code

6.3.響應式編程(Reactor 實現)

引入依賴

 <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
package com.ldp.reactor;

import org.junit.jupiter.api.Test;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

/**
 * @author 姿勢帝-博客園
 * @address https://www.cnblogs.com/newAndHui/
 * @WeChat 851298348
 * @create 01/15 7:40
 * @description * <p>
 * 單詞認識
 * 1.mono
 * 英 [ˈmɒnəʊ]   美 [ˈmɑːnoʊ]
 * adj.單聲道的   n.單聲道錄音(或放音)系統
 * 2.flux
 * 英 [flʌks]   美 [flʌks]
 * n.不斷的變動;不停的變化;通量;流動  v.熔化;熔解;流出
 * </P>
 * Publisher 發送(生產者,可以類比為把數據放入redis)
 * subscribe 訂閱(消費者,可以類比為從redis中讀取數據)
 * <p>
 * 1.響應式編程操作中,Reactor 是滿足 Reactive 規范框架
 * 2.Reactor 有兩個核心類,Mono 和 Flux,這兩個類實現接口 Publisher,提供豐富操作API;
 * 2.1.Flux 對象實現發布者,返回 N 個元素;
 * 2.2.Mono 實現發布者,返回 0 或者 1 個元素
 * <p>
 * 3.Flux 和 Mono 都是數據流的發布者,使用 Flux 和 Mono 都可以發出三種數據信號:
 * 元素值
 * 錯誤信號
 * 完成信號
 * 錯誤信號和完成信號:都代表終止信號,終止信號用於告訴訂閱者數據流結束了;
 * 錯誤信號終止數據流同時把錯誤信息傳遞給訂閱者;
 */
public class Test01Reactor {
    /**
     * 簡單使用
     */
    @Test
    public void test01() {
        // just方法直接聲明
        // 2.1.Flux 對象實現發布者,返回 N 個元素 (實際開發中我們可以放入產品列表數據)
        Flux.just("張三", "李四", "王五", "趙六").subscribe(System.out::println);
        System.out.println("====================================================");
        // 2.2.Mono 實現發布者,返回 0 或者 1 個元素 (實際開發中我們可以放入根據id查詢的單條數據)
        Mono.just("張無忌").subscribe(System.out::println);
    }

    /**
     * Flux 的formXXX聲明
     */
    @Test
    public void test02() {
        System.out.println("數組.....");
        Integer[] array = {1, 2, 3, 4};
        Flux.fromArray(array).subscribe(System.out::println);

        System.out.println("集合.....");
        List<Integer> list = Arrays.asList(array);
        Flux.fromIterable(list).subscribe(System.out::println);

        System.out.println("流.....");
        Stream<Integer> stream = list.stream();
        Flux.fromStream(stream).subscribe(System.out::println);
    }
}
View Code

6.4.SpringWebflux 執行流程和核心 API

SpringWebflux 執行過程和 SpringMVC 相似的

1.SpringWebflux 核心控制器 DispatchHandler,實現接口 WebHandler,WebHandler 有一個方法handle

SpringWebflux 里面 DispatcherHandler,負責請求的處理

1. HandlerMapping:請求查詢到處理的方法

2. HandlerAdapter:真正負責請求處理

3. HandlerResultHandler:響應結果處理

源碼解讀

6.5.SpringWebflux(基於注解編程模型)

用法與springmvc幾乎是一樣的,只是service的實現類有點不一樣,這里以Product模型案例講解

備注
SpringMVC 方式實現,同步阻塞的方式,基於 SpringMVC+Servlet+Tomcat
SpringWebflux 方式實現,異步非阻塞 方式,基於 SpringWebflux+Reactor+Netty

步驟一:編寫Product模型

package com.ldp.reactor.model;

/**
 * @author 姿勢帝-博客園
 * @address https://www.cnblogs.com/newAndHui/
 * @WeChat 851298348
 * @create 01/16 6:42
 * @description
 */
public class Product {
    private Integer id;
    private String productNo;
    private String productName;

    public Product() {
    }

    public Product(Integer id, String productNo, String productName) {
        this.id = id;
        this.productNo = productNo;
        this.productName = productName;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getProductNo() {
        return productNo;
    }

    public void setProductNo(String productNo) {
        this.productNo = productNo;
    }

    public String getProductName() {
        return productName;
    }

    public void setProductName(String productName) {
        this.productName = productName;
    }

    @Override
    public String toString() {
        return "Product{" +
                "id=" + id +
                ", productNo='" + productNo + '\'' +
                ", productName='" + productName + '\'' +
                '}';
    }
}
View Code

步驟二:編寫IProductService接口

package com.ldp.reactor.service;

import com.ldp.reactor.model.Product;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

/**
 * @author 姿勢帝-博客園
 * @address https://www.cnblogs.com/newAndHui/
 * @WeChat 851298348
 * @create 01/16 6:58
 * @description
 */
public interface IProductService {
    //根據 id 查詢用戶
    Mono<Product> getProductById(int id);

    //查詢所有用戶
    Flux<Product> getAllProduct();

    //添加用戶
    Mono<Void> saveProductInfo(Mono<Product> user);

}
View Code

步驟三:編寫ProductServiceImpl實現

注意:與springmvc相比較就是這里不一樣,因此在能熟練運用springmvc的情況下使用SpringWebflux,應該還是比較容易

package com.ldp.reactor.service.impl;

import com.ldp.reactor.model.Product;
import com.ldp.reactor.service.IProductService;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.HashMap;
import java.util.Map;

/**
 * @author 姿勢帝-博客園
 * @address https://www.cnblogs.com/newAndHui/
 * @WeChat 851298348
 * @create 01/16 7:01
 * @description
 */
@Service
public class ProductServiceImpl implements IProductService {
    //創建 map 集合存儲數據
    public Map<Integer, Product> products = new HashMap<>();

    public ProductServiceImpl() {
        for (int i = 1; i <= 10; i++) {
            this.products.put(i, new Product(i, "P1001" + i, "蘋果-" + i));
        }
    }

    @Override
    public Mono<Product> getProductById(int id) {
        return Mono.justOrEmpty(this.products.get(id));
    }

    @Override
    public Flux<Product> getAllProduct() {
        return Flux.fromIterable(this.products.values());
    }

    @Override
    public Mono<Void> saveProductInfo(Mono<Product> ProductMono) {
        return ProductMono.doOnNext(product -> {
            //向 map 集合里面放值
            int id = products.size() + 1;
            products.put(id, product);
        }).thenEmpty(Mono.empty());
    }
}
View Code

步驟四:ProductController的編寫

package com.ldp.reactor.controller;

import com.ldp.reactor.model.Product;
import com.ldp.reactor.service.IProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

/**
 * @author 姿勢帝-博客園
 * @address https://www.cnblogs.com/newAndHui/
 * @WeChat 851298348
 * @create 01/16 7:11
 * @description
 */
@RestController
public class ProductController {
    @Autowired
    private IProductService productService;

    /**
     * id 查詢
     *
     * @param id
     * @return
     */
    @GetMapping("/product/{id}")
    public Mono<Product> getProductId(@PathVariable int id) {
        return productService.getProductById(id);
    }

    /**
     * @return
     */
    @GetMapping("/product")
    public Flux<Product> getProducts() {
        return productService.getAllProduct();
    }

    /**
     * @param Product
     * @return
     */
    @PostMapping("/saveProduct")
    public Mono<Void> saveProduct(@RequestBody Product Product) {
        Mono<Product> ProductMono = Mono.just(Product);
        return productService.saveProductInfo(ProductMono);
    }
}
View Code

步驟五:編寫接口測試

package com.ldp.reactor.controller;


import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
import cn.hutool.http.Method;
import com.alibaba.fastjson.JSON;
import com.ldp.reactor.Base;
import org.junit.jupiter.api.Test;

import java.util.HashMap;
import java.util.Map;

/**
 * @author 姿勢帝-博客園
 * @address https://www.cnblogs.com/newAndHui/
 * @WeChat 851298348
 * @create 01/16 7:15
 * @description
 */
public class ProductControllerTest extends Base {

    @Test
    public void getProductId() {
        String url = urlLocal + "/product/11";
        System.out.println("請求地址:" + url);
        HttpRequest request = HttpUtil.createRequest(Method.GET, url);
        request.setConnectionTimeout(60 * 1000);
        String response = request.execute().body();
        System.out.println("響應結果:" + response);
    }

    @Test
    public void getProducts() {
        String url = urlLocal + "/product";
        System.out.println("請求地址:" + url);
        HttpRequest request = HttpUtil.createRequest(Method.GET, url);
        request.setConnectionTimeout(60 * 1000);
        String response = request.execute().body();
        System.out.println("響應結果:" + response);
    }

    @Test
    public void saveProduct() {
        String url = urlLocal + "/saveProduct";
        System.out.println("請求地址:" + url);
        HttpRequest request = HttpUtil.createRequest(Method.POST, url);
        Map<String, Object> map = new HashMap<>();
        map.put("productNo", "P203");
        map.put("productName", "習慣03");
        String jsonStr = JSON.toJSONString(map);
        request.body(jsonStr);
        System.out.println("請求參數:" + jsonStr);
        request.setConnectionTimeout(60 * 1000);
        String response = request.execute().body();
        System.out.println("響應結果:" + response);
    }
}
View Code

6.6.SpringWebflux(基於函數式編程模型)

1.在使用函數式編程模型操作時候,需要自己初始化服務器

2.基於函數式編程模型時候,有兩個核心接口:
RouterFunction(實現路由功能,請求轉發給對應的 handler)
HandlerFunction(處理請求生成響應的函數)。

3.核心任務定義兩個函數式接口的實現並且啟動需要的服務器。

4.SpringWebflux 請求和響應不再是ServletRequest和ServletResponse ,而是
ServerRequest 和 ServerResponse

在基於注解實現的情況下具體實現步驟如下

步驟一:編寫Handle處理器

package com.ldp.reactor.handler;

import com.ldp.reactor.model.Product;
import com.ldp.reactor.service.IProductService;
import com.ldp.reactor.service.impl.ProductServiceImpl;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import static org.springframework.web.reactive.function.BodyInserters.fromObject;

/**
 * @author 姿勢帝-博客園
 * @address https://www.cnblogs.com/newAndHui/
 * @WeChat 851298348
 * @create 01/16 7:51
 * @description
 */
public class ProductHandler {
    private IProductService productService;

    public ProductHandler() {
        this.productService = new ProductServiceImpl();
    }

    //根據 id 查詢
    public Mono<ServerResponse> getProductById(ServerRequest request) {
        //獲取 id 值
        int ProductId = Integer.valueOf(request.pathVariable("id"));
        //空值處理
        Mono<ServerResponse> notFound = ServerResponse.notFound().build();
        //調用 service 方法得到數據
        Mono<Product> productMono = this.productService.getProductById(ProductId);
        //把 ProductMono 進行轉換返回
        //使用 Reactor 操作符 flatMap
        return
                productMono
                        .flatMap(person ->
                                ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)
                                        .body(fromObject(person)))
                        .switchIfEmpty(notFound);
    }

    //查詢所有
    public Mono<ServerResponse> getAllProducts(ServerRequest serverRequest) {
        //調用 service 得到結果
        Flux<Product> products = this.productService.getAllProduct();
        return
                ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(products, Product.class);
    }

    //添加
    public Mono<ServerResponse> saveProduct(ServerRequest request) {
        //得到 Product 對象
        Mono<Product> ProductMono = request.bodyToMono(Product.class);
        return
                ServerResponse.ok().build(this.productService.saveProductInfo(ProductMono));
    }
}
View Code

步驟二:編寫啟動服務端

package com.ldp.reactor;

import com.ldp.reactor.handler.ProductHandler;
import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory;
import org.springframework.boot.web.reactive.server.ReactiveWebServerFactory;
import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.http.server.reactive.ReactorHttpHandlerAdapter;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.netty.http.server.HttpServer;

import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.web.reactive.function.server.RequestPredicates.*;

/**
 * @author 姿勢帝-博客園
 * @address https://www.cnblogs.com/newAndHui/
 * @WeChat 851298348
 * @create 01/16 7:58
 * @description
 */
public class FluxServer {
    //1 創建 Router 路由
    public RouterFunction<ServerResponse> routingFunction() {
        //創建 hanler 對象
        ProductHandler handler = new ProductHandler();
        //設置路由
        return RouterFunctions.route(
                GET("/product/{id}").and(accept(APPLICATION_JSON)), handler::getProductById)
                .andRoute(GET("/product").and(accept(APPLICATION_JSON)), handler::getAllProducts)
                .andRoute(POST("/saveProduct").and(accept(APPLICATION_JSON)), handler::saveProduct);
    }

    //2 創建服務器完成適配
    public void createReactorServer() {
        //路由和 handler 適配
        RouterFunction<ServerResponse> route = routingFunction();
        HttpHandler httpHandler = RouterFunctions.toHttpHandler(route);
        ReactorHttpHandlerAdapter adapter = new
                ReactorHttpHandlerAdapter(httpHandler);
        //創建服務器
        HttpServer httpServer = HttpServer.create();
        httpServer.handle(adapter).bindNow();
    }

    //最終調用
    public static void main(String[] args) throws Exception {
        FluxServer server = new FluxServer();
        server.createReactorServer();
        System.out.println("enter to exit");
        System.in.read();
    }
}
View Code

步驟三:測試

測試方式一:使用http模擬請求的方式,與springmvc的測試方式一樣(略)

測試方式二:使用flux客戶端的方式

package com.ldp.reactor;

import com.ldp.reactor.model.Product;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Flux;

/**
 * @author 姿勢帝-博客園
 * @address https://www.cnblogs.com/newAndHui/
 * @WeChat 851298348
 * @create 01/16 11:03
 * @description
 */
public class FluxClient {
    public static void main(String[] args) {
        //調用服務器地址
        WebClient webClient = WebClient.create("http://localhost:13220");
        //根據 id 查詢
        String id = "1";
        Product productResult = webClient.get().uri("/product/{id}", id)
                .accept(MediaType.APPLICATION_JSON).retrieve().bodyToMono(Product
                        .class)
                .block();
        System.out.println("查詢結果:" + productResult);
        //查詢所有
        Flux<Product> results = webClient.get().uri("/product")
                .accept(MediaType.APPLICATION_JSON).retrieve().bodyToFlux(Product.class);
        results.map(product -> product)
                .buffer().doOnNext(System.out::println).blockFirst();
    }
}
View Code

7.總結

若有不懂可以結合源碼和視頻教程學習!

完美!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM