Log4j2 官网下载地址
@Nullable 注解可以使用在方法上面,属性上面,参数上面,表示方法返回可以为空,属性值可以为空,参数值可以为空。
不指定 bean 名称
这里不指定名称的话不是默认为“类名首字母小写”,而是需要写全限定类名来获取。
@Test public void testGenericApplicationContextWithoutName(){ //1. 创建 GenericApplicationContext 对象 GenericApplicationContext context = new GenericApplicationContext(); //2-1 清空context context.refresh(); //2-2 注册 bean context.registerBean(Account.class,() -> new Account()); //3. 获取 bean Account account1 = context.getBean("com.hedon.bean.Account", Account.class); System.out.println(account1); }WebFlux 是 Spring5 添加新的模块,用于 web 开发的,功能和 SpringMVC 类似、Webflux 是当前一种比较流行的响应式编程框架。
传统 web 框架,比如 SpringMVC,这些基于 Servlet 容器,WebFlux 是一种异步非阻塞的框架,异步非阻塞的框架在 Servlet3.1 以后才支持,核心是基于 Reactor 的相关 API 实现的。
异步和同步针对调用者,调用者发送请求,如果等着对方回应之后才去做其他事情就是同步,如果发送请求之后不等着对方回应就去做其他事情就是异步。
阻塞和非阻塞针对被调用者,被调用者受到请求之后,做完请求任务之后才给出反馈就是阻塞,受到请求之后马上给出反馈然后再去做事情就是非阻塞。
在有限资源下,提高系统吞吐量和伸缩性,以 Reactor 为基础实现响应式编程。
Spring5 框架基于 java8,WebFlux 使用 Java8 函数式编程方式实现路由请求。
响应式编程是一种面向数据流和变化传播的编程范式。这意味着可以在编程语言中很方便地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播。
电子表格程序就是响应式编程的一个例子。单元格可以包含字面值或类似"=B1+C1"的公式,而包含公式的单元格的值会依据其他单元格的值的变化而变化。
Mono 和 Flux,这两个类实现接口 Publisher,提供丰富操作符。
Flux 对象实现发布者,返回 N 个元素;
Mono 实现发布者,返回 0 或者 1 个元素。
Flux 和 Mono 都是数据流的发布者,使用 Flux 和 Mono 都可以发出三种数据信号: 元素值,错误信号,完成信号。
错误信号和完成信号都代表终止信号,不能共存的。终止信号用于告诉订阅者数据流结束了,错误信号终止数据流同时把错误信息传递给订阅者。
如果没有发送任何元素值,而是直接发送错误或者完成信号,表示是空数据流。
如果没有错误信号,没有完成信号,表示是无限数据流。
引入 jar 包坐标
<dependency> <groupId>io.projectreactor</groupId> <artifactId>reactor-core</artifactId> <version>3.3.6.RELEASE</version> </dependency>编写程序代码
public class TestReactor { public static void main(String[] args) { /** * juts 方法直接申明 */ //Flux可以传多个元素 Flux.just(1,2,3,4); //Moon传1个或0个元素 Mono.just(1); /** * 其他方法 */ //数组形式的数据流 Integer[] array = {1,2,3,4}; Flux.fromArray(array); //集合形式的数据流 List<Integer> list = Arrays.asList(array); Flux.fromIterable(list); //Stream流 Stream<Integer> stream = list.stream(); Flux.fromStream(stream); } }注意:调用 just 或者其他方法只是声明数据流,数据流并没有发出,只有进行订阅之后才会触发数据流,不订阅什么都不会发生的。
上述程序直接运行,没有东西:
15:45:29.462 [main] DEBUG reactor.util.Loggers$LoggerFactory - Using Slf4j logging framework Process finished with exit code 0订阅一波:
//Flux可以传多个元素 Flux.just(1,2,3,4).subscribe(System.out::println); //Moon传1个或0个元素 Mono.just(1).subscribe(System.out::println);运行程序:
15:46:23.974 [main] DEBUG reactor.util.Loggers$LoggerFactory - Using Slf4j logging framework 1 2 3 4 1 Process finished with exit code 0对数据流进行一道道操作,成为操作符,比如工厂流水线。
SpringWebflux 基于 Reactor,默认使用容器是 Netty,Netty 是高性能的 NIO 框架,NIO 即同步非阻塞。
只能同时处理一个连接,要管理多个并发客户端,需要为每个新的客户端 Socket 创建一个新的 Thread,线程模型如下图所示:
这样就会存在以下问题:
在任何时候都可能有大量的线程处于休眠状态,只是等待输入或者输出数据就绪,这可能算是一种资源浪费。需要为每个线程的调用栈都分配内存。即使 Java 虚拟机(JVM) 在物理上可以支持非常大数量的线程, 但是远在到达该极限之前, 上下文切换所带来的开销就会带来麻烦。从该图可以看出Selector 是Java 的非阻塞 I/O 实现的关键。它使用了事件通知 API 以确定在一组非阻塞套接字中有哪些已经就绪能够进行 I/O 相关的操作。因为可以在任何的时间检查任意的读操作或者写操作的完成状态。该种模型下,一个单一的线程便可以处理多个并发的连接。
与BIO相比,该模型有以下特点:
使用较少的线程便可以处理许多连接,因此也减少了内存管理和上下文切换所带来开销。当没有 I/O 操作需要处理的时候,线程也可以被用于其他任务。虽然Java 的NIO在性能上比BIO已经相当的优秀,但是要做到如此正确和安全并不容易。特别是,在高负载下可靠和高效地处理和调度 I/O 操作是一项繁琐而且容易出错的任务,于是就有了 Netty。
Netty对NIO的API进行了封装,通过以下手段让性能又得到了一定程度的提升:
使用多路复用技术,提高处理连接的并发性。零拷贝。Netty的接收和发送数据采用DIRECT BUFFERS,使用堆外直接内存进行Socket读写,不需要进行字节缓冲区的二次拷贝。Netty提供了组合Buffer对象,可以聚合多个ByteBuffer对象进行一次操作。Netty的文件传输采用了transferTo方法,它可以直接将文件缓冲区的数据发送到目标Channel,避免了传统通过循环write方式导致的内存拷贝问题。内存池:为了减少堆外直接内存的分配和回收产生的资源损耗问题,Netty提供了基于内存池的缓冲区重用机制。使用主从Reactor多线程模型,提高并发性。采用了串行无锁化设计,在IO线程内部进行串行操作,避免多线程竞争导致的性能下降。默认使用Protobuf的序列化框架。灵活的TCP参数配置。参考1:https://blog.csdn.net/eric_sunah/article/details/80424344
参考2:https://www.infoq.cn/article/netty-high-performance/#anch111813
SpringWebflux 核心控制器是 DispatchHandler,该控制器负责请求的处理。它实现了接口 WebHandler,WebHandler 中只有一个方法 handler(),它在 DispatchHandler 中的实现如下::
public Mono<Void> handle(ServerWebExchange exchange) { //ServerWebExchange:存放http请求响应的信息 //如果 handlerMappings 为空,则返回一个 createNotFoundError 的错误 return this.handlerMappings == null ? this.createNotFoundError() : //如果不为空,则根据请求地址获取对应的 mapping Flux.fromIterable(this.handlerMappings).concatMap((mapping) -> { return mapping.getHandler(exchange); }).next().switchIfEmpty(this.createNotFoundError()).flatMap((handler) -> { //调用具体的业务方法 return this.invokeHandler(exchange, handler); }).flatMap((result) -> { //返回处理结果 return this.handleResult(exchange, result); }); }DispatchHandler 有 3 个属性:
public class DispatcherHandler implements WebHandler, ApplicationContextAware { @Nullable private List<HandlerMapping> handlerMappings; @Nullable private List<HandlerAdapter> handlerAdapters; @Nullable private List<HandlerResultHandler> resultHandlers; HandlerMapping:请求查询到处理的方法HandlerAdapter:真正负责请求处理HandlerResultHandler:响应结果处理将:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency>换成:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency>实体类 User
public class User { private String name; private String gender; private Integer age; //getter、setter //有参、无参构造 //toString() }创建接口 UserService,定义操作方法
public interface UserService { //根据id查询用户 Mono<User> getUserById(Integer id); //查询所有用户 Flux<User> getAllUsers(); //添加用户 Mono<Void> saveUserInfo(Mono<User> userMono); }创建接口实现类 UserServiceImpl,实现具体操作:
@Service public class UserServiceImpl implements UserService { //创建 map 集合存储数据 private final Map<Integer, User> users = new HashMap<>(); //初始化 public UserServiceImpl() { this.users.put(1, new User("lucy", "男", 20)); this.users.put(2, new User("mary", "女", 30)); this.users.put(3, new User("jack", "女", 50)); } //根据ID查询User @Override public Mono<User> getUserById(Integer id) { return Mono.justOrEmpty(this.users.get(id)); } //查询所有User @Override public Flux<User> getAllUsers() { return Flux.fromIterable(this.users.values()); } //保存 User @Override public Mono<Void> saveUserInfo(Mono<User> userMono) { return userMono.doOnNext(user -> { //向 map 中放值 int id = users.size()+1; users.put(id,user); }).thenEmpty(Mono.empty()); //操作后将 Mono 中的值清空(即发送终止信号) } }运行: