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>
步驟三:測試

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........."); } }
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); } }
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); } }
二者在使用上的區別
個人覺得在常用的功能上沒有什么大的區別,只是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); }
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"); } }
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); } }
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 + '\'' + '}'; } }
步驟二:編寫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); }
步驟三:編寫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()); } }
步驟四: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); } }
步驟五:編寫接口測試

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); } }
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)); } }
步驟二:編寫啟動服務端

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(); } }
步驟三:測試
測試方式一:使用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(); } }
7.總結
若有不懂可以結合源碼和視頻教程學習!