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>
步骤三:测试
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.总结
若有不懂可以结合源码和视频教程学习!
完美!
spring5的新特性的更多相关文章
- spring5.0新特性
spring5.0新特性 学习了:http://blog.csdn.net/u012562943/article/details/77449666 https://www.cnblogs.com/xu ...
- Spring5 新特性
Spring Framework 5.0是在Spring Framework 4.0之后将近四年内一次重大的升级. 最大特点之一是响应式编程(Reactive Programming). 响应式编程核 ...
- 详细介绍Spring 5的那些新特性与增强
Spring5 是一个重要的版本,距离SpringFramework4差不多四年.在此期间,大多数增强都是在 SpringBoot 项目中完成的.在本文中,我们将很快了解到Spring5发行版中的一些 ...
- 业余草分享 Spring Boot 2.0 正式发布的新特性
就在昨天Spring Boot2.0.0.RELEASE正式发布,今天早上在发布Spring Boot2.0的时候还出现一个小插曲,将Spring Boot2.0同步到Maven仓库的时候出现了错误, ...
- Spring Boot 2(一):Spring Boot 2.0新特性
Spring Boot 2(一):Spring Boot 2.0新特性 Spring Boot依赖于Spring,而Spring Cloud又依赖于Spring Boot,因此Spring Boot2 ...
- 我这样回答了Spring 5的新特性,面试官对我刮目相看
最近,有一个小伙伴拿到了自己满意的Offer,和他交谈的过程中得知他面试官问他关于Spring的问题比较多,其中最让面试官满意的就是自己回答关于Spring 5的知识点回答的不错. Spring5于2 ...
- SQL Server 2014 新特性——内存数据库
SQL Server 2014 新特性——内存数据库 目录 SQL Server 2014 新特性——内存数据库 简介: 设计目的和原因: 专业名词 In-Memory OLTP不同之处 内存优化表 ...
- ElasticSearch 5学习(10)——结构化查询(包括新特性)
之前我们所有的查询都属于命令行查询,但是不利于复杂的查询,而且一般在项目开发中不使用命令行查询方式,只有在调试测试时使用简单命令行查询,但是,如果想要善用搜索,我们必须使用请求体查询(request ...
- [干货来袭]C#6.0新特性
微软昨天发布了新的VS 2015 ..随之而来的还有很多很多东西... .NET新版本 ASP.NET新版本...等等..太多..实在没消化.. 分享一下也是昨天发布的新的C#6.0的部分新特性吧.. ...
- CSS3新特性应用之结构与布局
一.自适应内部元素 利用width的新特性min-content实现 width新特性值介绍: fill-available,自动填充盒子模型中剩余的宽度,包含margin.padding.borde ...
随机推荐
- Angular 集成 StreamSaver
应用场景: 实现目标: 在网页端实现大文件(文件大小 >= 2 G) 断点续传 实际方案: 发送多次请求, 每次请求一部分文件数据, 然后通过续写将文件数据全部写入. 难点: 无法实现文件续写, ...
- 认真学习css3--01-全是日志没有技术内容
由于某些原因,还是要写写前端代码. 虽然这不是我的主要责任,但也有表率的必要性. css3这个东西没有什么大难度,难点主要集中在: 1)样式比较多 2)精准定位 3)性能优化 其中第2个是日常经常面对 ...
- FFmpeg开发笔记(三十二)利用RTMP协议构建电脑与手机的直播Demo
不管是传统互联网还是移动互联网,实时数据传输都是刚需,比如以QQ.微信为代表的即时通信工具,能够实时传输文本和图片.其中一对一的图文通信叫做私聊,多对多的图文通信叫做群聊. 除了常见的图文即时通信,还 ...
- adb连接安卓设备失败failed to start daemon
adb连接安卓设备失败failed to start daemon Reference:https://blog.csdn.net/whshuo2010/article/details/5109449 ...
- ARM 命名规则和ARM 版本
结论:我们所接触到提到的命名规则,应该分成两类. 基于ARM Architecture版本的"指令集架构"命名规则:例如armv6, armv7, armv7s, arm64 等系 ...
- Qt 之 emit、signals、slot的使用
背景 ref : https://www.ibm.com/developerworks/cn/linux/guitoolkit/qt/signal-slot/index.html 信号和槽机制是 QT ...
- dig 命令详解及使用示例
一.背景知识 CNAME CNAME 是 Canonical Name 的缩写,它是 DNS(域名系统)记录的一种类型.CNAME 记录用于将一个域名映射(别名)到另一个域名.换句话说,当你访问一个设 ...
- InvocationTargetException和UndeclaredThrowableException异常介绍
今天来介绍了两个陌生又熟悉的异常类,熟悉是因为我们经常会遇到它们,陌生是好像又从来不知道它们是做什么的 假定读者已经清楚了Java的异常分类: 一是程序不能处理的错误(Error), 二是程序应该避免 ...
- python rsa-oaep加密示例
代码: 1 from Crypto.PublicKey import RSA 2 from Crypto.Cipher import PKCS1_OAEP 3 import base64 4 rsa_ ...
- LitCTF 2023 部分wp
LitCTF 2023 PWN 只需要nc一下~ 根目录下的是假flag,真的在环境变量里 口算题卡 简单的计算题 import pwn io = pwn.remote("node5.ann ...