1.背景

2.依赖环境的变化

整个 Spring5 框架的代码基于 Java8,运行时兼容 JDK9,许多不建议使用的类和方 法在代码库中删除

3.自带了通用的日志封装

3.1.日志的简单使用

Spring 5.x框架自带了通用的日志封装
(1)Spring5 已经移除 Log4jConfigListener,官方建议使用 Log4j2
(2)Spring5 框架整合 Log4j2
使用步骤:
步骤一: 引入 jar 包
spring5的新特性 - 图1
步骤二: 创建 log4j2.xml 配置文件

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE >
  3. ALL -->
  4. <!--Configuration 后面的 status 用于设置 log4j2 自身内部的信息输出,可以不设置,
  5. 当设置成 trace 时,可以看到 log4j2 内部各种详细输出-->
  6. <configuration status="INFO">
  7. <!--先定义所有的 appender-->
  8. <appenders>
  9. <!--输出日志信息到控制台-->
  10. <console name="Console" target="SYSTEM_OUT">
  11. <!--
  12. 日志输出格式:
  13. %d表示日期时间,
  14. %thread表示线程名,
  15. %-5level:级别从左显示5个字符宽度
  16. %logger{50} 表示logger名字最长50个字符,否则按照句点分割。
  17. %msg:日志消息,
  18. %n是换行符
  19. -->
  20. <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%-5level] %logger{36} - %msg%n"/>
  21. </console>
  22. </appenders>
  23. <!--然后定义 logger,只有定义 logger 并引入的 appenderappender 才会生效-->
  24. <!--root:用于指定项目的根日志,如果没有单独指定 Logger,则会使用 root 作为
  25. 默认的日志输出-->
  26. <loggers>
  27. <root level="info">
  28. <appender-ref ref="Console"/>
  29. </root>
  30. </loggers>
  31. </configuration>

步骤三:测试

  1. package com.ldp.test;
  2. import org.slf4j.Logger;
  3. import org.slf4j.LoggerFactory;
  4. /**
  5. * @author 姿势帝-博客园
  6. * @address https://www.cnblogs.com/newAndHui/
  7. * @WeChat 851298348
  8. * @create 01/14 7:07
  9. * @description
  10. */
  11. public class TestLog4j2 {
  12. private static final Logger log = LoggerFactory.getLogger(TestLog4j2.class);
  13. /**
  14. * log4j2测试
  15. * 日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE >
  16. *
  17. * @param args
  18. */
  19. public static void main(String[] args) {
  20. log.error("error......");
  21. log.warn("warn......");
  22. log.info("info......");
  23. log.debug("debug......");
  24. log.trace("trace.........");
  25. }
  26. }

3.2.spring中的测试

1.原来在spring中的测试

  1. package com.ldp.test;
  2. import com.ldp.model.User;
  3. import org.junit.Test;
  4. import org.junit.runner.RunWith;
  5. import org.springframework.beans.factory.annotation.Autowired;
  6. import org.springframework.test.context.ContextConfiguration;
  7. import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
  8. /**
  9. * @author 姿势帝-博客园
  10. * @address https://www.cnblogs.com/newAndHui/
  11. * @WeChat 851298348
  12. * @create 01/14 7:24
  13. * @description
  14. */
  15. // junit4的测试方式
  16. @RunWith(SpringJUnit4ClassRunner.class)
  17. // 加载配置文件
  18. @ContextConfiguration("classpath:bean1.xml")
  19. public class TestJunit4 {
  20. @Autowired
  21. private User user;
  22. @Test
  23. public void test01() {
  24. System.out.println(user);
  25. }
  26. }

2.spring5中的测试

  1. package com.ldp.test;
  2. import com.ldp.model.User;
  3. import org.junit.jupiter.api.Test;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
  6. /**
  7. * @author 姿势帝-博客园
  8. * @address https://www.cnblogs.com/newAndHui/
  9. * @WeChat 851298348
  10. * @create 01/14 7:24
  11. * @description
  12. */
  13. /**
  14. * junit4的测试方式
  15. *
  16. * @RunWith(SpringJUnit4ClassRunner.class)
  17. * @ContextConfiguration("classpath:bean1.xml")
  18. */
  19. // spring5的测试
  20. @SpringJUnitConfig(locations = "classpath:bean1.xml")
  21. public class TestJunit5 {
  22. @Autowired
  23. private User user;
  24. /**
  25. * 注意spring5中的这个@Test注解是 import org.junit.jupiter.api.Test;
  26. * 而原来使用的的这个@Test注解是 import org.junit.Test;
  27. */
  28. @Test
  29. public void test01() {
  30. System.out.println(user);
  31. }
  32. }

二者在使用上的区别
spring5的新特性 - 图2
个人觉得在常用的功能上没有什么大的区别,只是Junit5写起来简洁一点

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

@Nullable 注解可以使用在方法上面,属性上面,参数上面,表示方法返回可以为空,属性值可以为空,参数值可以为空

5.Spring5 核心容器支持函数式风格 GenericApplicationContext

  1. /**
  2. * 函数创建对象演示
  3. *
  4. * @param args
  5. */
  6. public static void main(String[] args) {
  7. //1 创建 GenericApplicationContext 对象
  8. GenericApplicationContext context = new GenericApplicationContext();
  9. //2 调用 context 的方法对象注册
  10. context.refresh();
  11. context.registerBean("user1", User.class, () -> new User());
  12. //3 获取在 spring 注册的对象
  13. // User user = (User)context.getBean("com.atguigu.spring5.test.User");
  14. User user = (User) context.getBean("user1");
  15. System.out.println(user);
  16. }

6.Spring5 框架新功能(Webflux)

6.1.SpringWebflux 介绍

(1)webFlux是 Spring5 添加的新模块,用于 web 的开发,功能和 SpringMVC 类似的,Webflux 使用 当前一种比较流程响应式编程出现的框架。
在spring5的jar包中和架构图中我们都可以看见
spring5的新特性 - 图3
(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 采用异步响应式编程
spring5的新特性 - 图4
具体的可以看后面的案例演示,可能会更好理解!
(6)总结(面试的时候很重要)
特点1.webflux是一个异步非阻塞的Web框架,它能够充分利用多核CPU的硬件资源去处理大量的并发请求
2.内部使用的是响应式编程,以Reactor库为基础,基于异步和事件驱动,可以让我们在不扩充硬件资源的前提下,提升系统的吞吐量和伸缩性。
3.不能使接口的响应时间缩短,它仅仅能够提升吞吐量和伸缩性。
应用场景1.特别适合在IO密集型的服务中,比如微服务网关。
2.IO 密集型包括:磁盘IO密集型, 网络IO密集型,
微服务网关就属于网络 IO 密集型,使用异步非阻塞式编程模型,能够显著地提升网关对下游服务转发的吞吐量。
选WebFlux还是Spring MVC1.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 实现)

  1. package com.ldp.testWebflux;
  2. import java.util.Observable;
  3. /**
  4. * @author 姿势帝-博客园
  5. * @address https://www.cnblogs.com/newAndHui/
  6. * @WeChat 851298348
  7. * @create 01/15 7:14
  8. * @description <p>
  9. * 什么是响应式编程
  10. * 响应式编程是一种面向数据流和变化传播的编程范式。这意味着可以在编程语言中很方便
  11. * 地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播。
  12. * 电子表格程序就是响应式编程的一个例子。单元格可以包含字面值或类似"=B1+C1"的公
  13. * 式,而包含公式的单元格的值会依据其他单元格的值的变化而变化。
  14. * </p>
  15. */
  16. public class Test01Observer extends Observable {
  17. /**
  18. * @param args
  19. */
  20. public static void main(String[] args) {
  21. Test01Observer observer = new Test01Observer();
  22. // 添加观察者
  23. observer.addObserver((o, arg) -> {
  24. System.out.println("通知数据变化---1");
  25. });
  26. observer.addObserver((o, arg) -> {
  27. System.out.println("通知数据变化---2");
  28. });
  29. observer.addObserver((o, arg) -> {
  30. System.out.println("通知数据变化---3");
  31. });
  32. // 数据变化
  33. observer.setChanged();
  34. // 通知
  35. observer.notifyObservers();
  36. System.out.println("代码执行完成.....4");
  37. }
  38. }

6.3.响应式编程(Reactor 实现)

引入依赖

org.springframework.boot
spring-boot-starter-webflux

  1. package com.ldp.reactor;
  2. import org.junit.jupiter.api.Test;
  3. import reactor.core.publisher.Flux;
  4. import reactor.core.publisher.Mono;
  5. import java.util.Arrays;
  6. import java.util.List;
  7. import java.util.stream.Stream;
  8. /**
  9. * @author 姿势帝-博客园
  10. * @address https://www.cnblogs.com/newAndHui/
  11. * @WeChat 851298348
  12. * @create 01/15 7:40
  13. * @description * <p>
  14. * 单词认识
  15. * 1.mono
  16. * 英 [ˈmɒnəʊ] 美 [ˈmɑːnoʊ]
  17. * adj.单声道的 n.单声道录音(或放音)系统
  18. * 2.flux
  19. * 英 [flʌks] 美 [flʌks]
  20. * n.不断的变动;不停的变化;通量;流动 v.熔化;熔解;流出
  21. * </P>
  22. * Publisher 发送(生产者,可以类比为把数据放入redis)
  23. * subscribe 订阅(消费者,可以类比为从redis中读取数据)
  24. * <p>
  25. * 1.响应式编程操作中,Reactor 是满足 Reactive 规范框架
  26. * 2.Reactor 有两个核心类,Mono 和 Flux,这两个类实现接口 Publisher,提供丰富操作API;
  27. * 2.1.Flux 对象实现发布者,返回 N 个元素;
  28. * 2.2.Mono 实现发布者,返回 0 或者 1 个元素
  29. * <p>
  30. * 3.Flux 和 Mono 都是数据流的发布者,使用 Flux 和 Mono 都可以发出三种数据信号:
  31. * 元素值
  32. * 错误信号
  33. * 完成信号
  34. * 错误信号和完成信号:都代表终止信号,终止信号用于告诉订阅者数据流结束了;
  35. * 错误信号终止数据流同时把错误信息传递给订阅者;
  36. */
  37. public class Test01Reactor {
  38. /**
  39. * 简单使用
  40. */
  41. @Test
  42. public void test01() {
  43. // just方法直接声明
  44. // 2.1.Flux 对象实现发布者,返回 N 个元素 (实际开发中我们可以放入产品列表数据)
  45. Flux.just("张三", "李四", "王五", "赵六").subscribe(System.out::println);
  46. System.out.println("====================================================");
  47. // 2.2.Mono 实现发布者,返回 0 或者 1 个元素 (实际开发中我们可以放入根据id查询的单条数据)
  48. Mono.just("张无忌").subscribe(System.out::println);
  49. }
  50. /**
  51. * Flux 的formXXX声明
  52. */
  53. @Test
  54. public void test02() {
  55. System.out.println("数组.....");
  56. Integer[] array = {1, 2, 3, 4};
  57. Flux.fromArray(array).subscribe(System.out::println);
  58. System.out.println("集合.....");
  59. List<Integer> list = Arrays.asList(array);
  60. Flux.fromIterable(list).subscribe(System.out::println);
  61. System.out.println("流.....");
  62. Stream<Integer> stream = list.stream();
  63. Flux.fromStream(stream).subscribe(System.out::println);
  64. }
  65. }

6.4.SpringWebflux 执行流程和核心 API

SpringWebflux 执行过程和 SpringMVC 相似的
1.SpringWebflux 核心控制器 DispatchHandler,实现接口 WebHandler,WebHandler 有一个方法handle
SpringWebflux 里面 DispatcherHandler,负责请求的处理
1. HandlerMapping:请求查询到处理的方法
2. HandlerAdapter:真正负责请求处理
3. HandlerResultHandler:响应结果处理
源码解读
spring5的新特性 - 图5

6.5.SpringWebflux(基于注解编程模型)

用法与springmvc几乎是一样的,只是service的实现类有点不一样,这里以Product模型案例讲解
备注SpringMVC 方式实现,同步阻塞的方式,基于 SpringMVC+Servlet+TomcatSpringWebflux 方式实现,异步非阻塞 方式,基于 SpringWebflux+Reactor+Netty
步骤一:编写Product模型

  1. package com.ldp.reactor.model;
  2. /**
  3. * @author 姿势帝-博客园
  4. * @address https://www.cnblogs.com/newAndHui/
  5. * @WeChat 851298348
  6. * @create 01/16 6:42
  7. * @description
  8. */
  9. public class Product {
  10. private Integer id;
  11. private String productNo;
  12. private String productName;
  13. public Product() {
  14. }
  15. public Product(Integer id, String productNo, String productName) {
  16. this.id = id;
  17. this.productNo = productNo;
  18. this.productName = productName;
  19. }
  20. public Integer getId() {
  21. return id;
  22. }
  23. public void setId(Integer id) {
  24. this.id = id;
  25. }
  26. public String getProductNo() {
  27. return productNo;
  28. }
  29. public void setProductNo(String productNo) {
  30. this.productNo = productNo;
  31. }
  32. public String getProductName() {
  33. return productName;
  34. }
  35. public void setProductName(String productName) {
  36. this.productName = productName;
  37. }
  38. @Override
  39. public String toString() {
  40. return "Product{" +
  41. "id=" + id +
  42. ", productNo='" + productNo + '\'' +
  43. ", productName='" + productName + '\'' +
  44. '}';
  45. }
  46. }

步骤二:编写IProductService接口

  1. package com.ldp.reactor.service;
  2. import com.ldp.reactor.model.Product;
  3. import reactor.core.publisher.Flux;
  4. import reactor.core.publisher.Mono;
  5. /**
  6. * @author 姿势帝-博客园
  7. * @address https://www.cnblogs.com/newAndHui/
  8. * @WeChat 851298348
  9. * @create 01/16 6:58
  10. * @description
  11. */
  12. public interface IProductService {
  13. //根据 id 查询用户
  14. Mono<Product> getProductById(int id);
  15. //查询所有用户
  16. Flux<Product> getAllProduct();
  17. //添加用户
  18. Mono<Void> saveProductInfo(Mono<Product> user);
  19. }

步骤三:编写ProductServiceImpl实现
注意:与springmvc相比较就是这里不一样,因此在能熟练运用springmvc的情况下使用SpringWebflux,应该还是比较容易

  1. package com.ldp.reactor.service.impl;
  2. import com.ldp.reactor.model.Product;
  3. import com.ldp.reactor.service.IProductService;
  4. import org.springframework.stereotype.Service;
  5. import reactor.core.publisher.Flux;
  6. import reactor.core.publisher.Mono;
  7. import java.util.HashMap;
  8. import java.util.Map;
  9. /**
  10. * @author 姿势帝-博客园
  11. * @address https://www.cnblogs.com/newAndHui/
  12. * @WeChat 851298348
  13. * @create 01/16 7:01
  14. * @description
  15. */
  16. @Service
  17. public class ProductServiceImpl implements IProductService {
  18. //创建 map 集合存储数据
  19. public Map<Integer, Product> products = new HashMap<>();
  20. public ProductServiceImpl() {
  21. for (int i = 1; i <= 10; i++) {
  22. this.products.put(i, new Product(i, "P1001" + i, "苹果-" + i));
  23. }
  24. }
  25. @Override
  26. public Mono<Product> getProductById(int id) {
  27. return Mono.justOrEmpty(this.products.get(id));
  28. }
  29. @Override
  30. public Flux<Product> getAllProduct() {
  31. return Flux.fromIterable(this.products.values());
  32. }
  33. @Override
  34. public Mono<Void> saveProductInfo(Mono<Product> ProductMono) {
  35. return ProductMono.doOnNext(product -> {
  36. //向 map 集合里面放值
  37. int id = products.size() + 1;
  38. products.put(id, product);
  39. }).thenEmpty(Mono.empty());
  40. }
  41. }

步骤四:ProductController的编写

  1. package com.ldp.reactor.controller;
  2. import com.ldp.reactor.model.Product;
  3. import com.ldp.reactor.service.IProductService;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.web.bind.annotation.*;
  6. import reactor.core.publisher.Flux;
  7. import reactor.core.publisher.Mono;
  8. /**
  9. * @author 姿势帝-博客园
  10. * @address https://www.cnblogs.com/newAndHui/
  11. * @WeChat 851298348
  12. * @create 01/16 7:11
  13. * @description
  14. */
  15. @RestController
  16. public class ProductController {
  17. @Autowired
  18. private IProductService productService;
  19. /**
  20. * id 查询
  21. *
  22. * @param id
  23. * @return
  24. */
  25. @GetMapping("/product/{id}")
  26. public Mono<Product> getProductId(@PathVariable int id) {
  27. return productService.getProductById(id);
  28. }
  29. /**
  30. * @return
  31. */
  32. @GetMapping("/product")
  33. public Flux<Product> getProducts() {
  34. return productService.getAllProduct();
  35. }
  36. /**
  37. * @param Product
  38. * @return
  39. */
  40. @PostMapping("/saveProduct")
  41. public Mono<Void> saveProduct(@RequestBody Product Product) {
  42. Mono<Product> ProductMono = Mono.just(Product);
  43. return productService.saveProductInfo(ProductMono);
  44. }
  45. }

步骤五:编写接口测试

  1. package com.ldp.reactor.controller;
  2. import cn.hutool.http.HttpRequest;
  3. import cn.hutool.http.HttpUtil;
  4. import cn.hutool.http.Method;
  5. import com.alibaba.fastjson.JSON;
  6. import com.ldp.reactor.Base;
  7. import org.junit.jupiter.api.Test;
  8. import java.util.HashMap;
  9. import java.util.Map;
  10. /**
  11. * @author 姿势帝-博客园
  12. * @address https://www.cnblogs.com/newAndHui/
  13. * @WeChat 851298348
  14. * @create 01/16 7:15
  15. * @description
  16. */
  17. public class ProductControllerTest extends Base {
  18. @Test
  19. public void getProductId() {
  20. String url = urlLocal + "/product/11";
  21. System.out.println("请求地址:" + url);
  22. HttpRequest request = HttpUtil.createRequest(Method.GET, url);
  23. request.setConnectionTimeout(60 * 1000);
  24. String response = request.execute().body();
  25. System.out.println("响应结果:" + response);
  26. }
  27. @Test
  28. public void getProducts() {
  29. String url = urlLocal + "/product";
  30. System.out.println("请求地址:" + url);
  31. HttpRequest request = HttpUtil.createRequest(Method.GET, url);
  32. request.setConnectionTimeout(60 * 1000);
  33. String response = request.execute().body();
  34. System.out.println("响应结果:" + response);
  35. }
  36. @Test
  37. public void saveProduct() {
  38. String url = urlLocal + "/saveProduct";
  39. System.out.println("请求地址:" + url);
  40. HttpRequest request = HttpUtil.createRequest(Method.POST, url);
  41. Map<String, Object> map = new HashMap<>();
  42. map.put("productNo", "P203");
  43. map.put("productName", "习惯03");
  44. String jsonStr = JSON.toJSONString(map);
  45. request.body(jsonStr);
  46. System.out.println("请求参数:" + jsonStr);
  47. request.setConnectionTimeout(60 * 1000);
  48. String response = request.execute().body();
  49. System.out.println("响应结果:" + response);
  50. }
  51. }

6.6.SpringWebflux(基于函数式编程模型)

1.在使用函数式编程模型操作时候,需要自己初始化服务器
2.基于函数式编程模型时候,有两个核心接口:RouterFunction(实现路由功能,请求转发给对应的 handler)HandlerFunction(处理请求生成响应的函数)。
3.核心任务定义两个函数式接口的实现并且启动需要的服务器。
4.SpringWebflux 请求和响应不再是ServletRequest和ServletResponse ,而是ServerRequest 和 ServerResponse
在基于注解实现的情况下具体实现步骤如下
步骤一:编写Handle处理器

  1. package com.ldp.reactor.handler;
  2. import com.ldp.reactor.model.Product;
  3. import com.ldp.reactor.service.IProductService;
  4. import com.ldp.reactor.service.impl.ProductServiceImpl;
  5. import org.springframework.http.MediaType;
  6. import org.springframework.web.reactive.function.server.ServerRequest;
  7. import org.springframework.web.reactive.function.server.ServerResponse;
  8. import reactor.core.publisher.Flux;
  9. import reactor.core.publisher.Mono;
  10. import static org.springframework.web.reactive.function.BodyInserters.fromObject;
  11. /**
  12. * @author 姿势帝-博客园
  13. * @address https://www.cnblogs.com/newAndHui/
  14. * @WeChat 851298348
  15. * @create 01/16 7:51
  16. * @description
  17. */
  18. public class ProductHandler {
  19. private IProductService productService;
  20. public ProductHandler() {
  21. this.productService = new ProductServiceImpl();
  22. }
  23. //根据 id 查询
  24. public Mono<ServerResponse> getProductById(ServerRequest request) {
  25. //获取 id 值
  26. int ProductId = Integer.valueOf(request.pathVariable("id"));
  27. //空值处理
  28. Mono<ServerResponse> notFound = ServerResponse.notFound().build();
  29. //调用 service 方法得到数据
  30. Mono<Product> productMono = this.productService.getProductById(ProductId);
  31. //把 ProductMono 进行转换返回
  32. //使用 Reactor 操作符 flatMap
  33. return
  34. productMono
  35. .flatMap(person ->
  36. ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)
  37. .body(fromObject(person)))
  38. .switchIfEmpty(notFound);
  39. }
  40. //查询所有
  41. public Mono<ServerResponse> getAllProducts(ServerRequest serverRequest) {
  42. //调用 service 得到结果
  43. Flux<Product> products = this.productService.getAllProduct();
  44. return
  45. ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(products, Product.class);
  46. }
  47. //添加
  48. public Mono<ServerResponse> saveProduct(ServerRequest request) {
  49. //得到 Product 对象
  50. Mono<Product> ProductMono = request.bodyToMono(Product.class);
  51. return
  52. ServerResponse.ok().build(this.productService.saveProductInfo(ProductMono));
  53. }
  54. }

步骤二:编写启动服务端

  1. package com.ldp.reactor;
  2. import com.ldp.reactor.handler.ProductHandler;
  3. import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory;
  4. import org.springframework.boot.web.reactive.server.ReactiveWebServerFactory;
  5. import org.springframework.http.server.reactive.HttpHandler;
  6. import org.springframework.http.server.reactive.ReactorHttpHandlerAdapter;
  7. import org.springframework.web.reactive.function.server.RouterFunction;
  8. import org.springframework.web.reactive.function.server.RouterFunctions;
  9. import org.springframework.web.reactive.function.server.ServerResponse;
  10. import reactor.netty.http.server.HttpServer;
  11. import static org.springframework.http.MediaType.APPLICATION_JSON;
  12. import static org.springframework.web.reactive.function.server.RequestPredicates.*;
  13. /**
  14. * @author 姿势帝-博客园
  15. * @address https://www.cnblogs.com/newAndHui/
  16. * @WeChat 851298348
  17. * @create 01/16 7:58
  18. * @description
  19. */
  20. public class FluxServer {
  21. //1 创建 Router 路由
  22. public RouterFunction<ServerResponse> routingFunction() {
  23. //创建 hanler 对象
  24. ProductHandler handler = new ProductHandler();
  25. //设置路由
  26. return RouterFunctions.route(
  27. GET("/product/{id}").and(accept(APPLICATION_JSON)), handler::getProductById)
  28. .andRoute(GET("/product").and(accept(APPLICATION_JSON)), handler::getAllProducts)
  29. .andRoute(POST("/saveProduct").and(accept(APPLICATION_JSON)), handler::saveProduct);
  30. }
  31. //2 创建服务器完成适配
  32. public void createReactorServer() {
  33. //路由和 handler 适配
  34. RouterFunction<ServerResponse> route = routingFunction();
  35. HttpHandler httpHandler = RouterFunctions.toHttpHandler(route);
  36. ReactorHttpHandlerAdapter adapter = new
  37. ReactorHttpHandlerAdapter(httpHandler);
  38. //创建服务器
  39. HttpServer httpServer = HttpServer.create();
  40. httpServer.handle(adapter).bindNow();
  41. }
  42. //最终调用
  43. public static void main(String[] args) throws Exception {
  44. FluxServer server = new FluxServer();
  45. server.createReactorServer();
  46. System.out.println("enter to exit");
  47. System.in.read();
  48. }
  49. }

步骤三:测试
测试方式一:使用http模拟请求的方式,与springmvc的测试方式一样(略)
测试方式二:使用flux客户端的方式

  1. package com.ldp.reactor;
  2. import com.ldp.reactor.model.Product;
  3. import org.springframework.http.MediaType;
  4. import org.springframework.web.reactive.function.client.WebClient;
  5. import reactor.core.publisher.Flux;
  6. /**
  7. * @author 姿势帝-博客园
  8. * @address https://www.cnblogs.com/newAndHui/
  9. * @WeChat 851298348
  10. * @create 01/16 11:03
  11. * @description
  12. */
  13. public class FluxClient {
  14. public static void main(String[] args) {
  15. //调用服务器地址
  16. WebClient webClient = WebClient.create("http://localhost:13220");
  17. //根据 id 查询
  18. String id = "1";
  19. Product productResult = webClient.get().uri("/product/{id}", id)
  20. .accept(MediaType.APPLICATION_JSON).retrieve().bodyToMono(Product
  21. .class)
  22. .block();
  23. System.out.println("查询结果:" + productResult);
  24. //查询所有
  25. Flux<Product> results = webClient.get().uri("/product")
  26. .accept(MediaType.APPLICATION_JSON).retrieve().bodyToFlux(Product.class);
  27. results.map(product -> product)
  28. .buffer().doOnNext(System.out::println).blockFirst();
  29. }
  30. }

7.总结

若有不懂可以结合源码和视频教程学习!

完美!