1 概述
-
整个Spring5框架基于JDK8,运行时兼容JDK9,许多不建议使用的类和方法在代码库中删除。
-
Spring5框架自带了通用的日志封装。
-
- Spring5框架已经移除了Log4jConfigListener,官方建议使用Log4j2。
-
Spring5框架核心容器支持@Nullable注解。
-
- @Nullable可以使用在方法、属性和参数上面。
-
-
- 如果@Nullable用在方法上面,表示方法的返回值可以为null。
-
-
-
- 如果@Nullable用在属性上面,表示属性值可以为null。
-
-
-
- 如果@Nullable用在方法参数里面,表示参数可以为null。
-
-
Spring5核心容器支持函数式风格GenericApplicationContext/AnnotationConfigApplicationContext。
-
Spring5支持整合Junit5。
-
Spring5支持WebFlux。
2 Spring5整合Log4j2
- 导入log4j相关jar包的Maven坐标
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.13.3</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.13.3</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.30</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.13.3</version>
</dependency>
- 创建log4j2的配置文件(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">
<!--控制日志输出的格式-->
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-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 top.open1024.spring;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Spring5Test {
private static final Logger LOGGER = LoggerFactory.getLogger(Spring5Test.class);
@Test
public void test() {
LOGGER.info("info....");
LOGGER.debug("debug...");
}
}
3 Spring5支持函数式风格
-
示例:
-
Person.java
package top.open1024.spring;
import org.springframework.stereotype.Component;
@Component
public class Person {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
}
- 测试:
package top.open1024.spring;
import org.junit.Test;
import org.springframework.context.support.GenericApplicationContext;
public class Spring5Test {
/**
* 函数式风格创建对象,交给Spring管理
*/
@Test
public void test() {
//创建GenericApplicationContext对象
GenericApplicationContext context = new GenericApplicationContext();
//调用context的refresh()方法
context.refresh();
context.registerBean("person",Person.class,()->new Person());
//获取在Spring注册的对象
Person person = context.getBean(Person.class);
System.out.println(person);
}
}
4 Spring5整合Junit
4.1 Spring5整合Junit4
- 导入junit4和Spring整合junit4相关jar包的Maven坐标:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
- 在测试类中,使用注解方式完成
package top.open1024.spring;
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;
//指定Junit的版本
@RunWith(SpringJUnit4ClassRunner.class)
//加载配置文件
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class Spring5Test {
@Autowired
private Person person;
@Test
public void test(){
System.out.println(person);
}
}
4.2 Spring5整合Junit5
- 导入junit5和Spring整合junit4相关jar包的Maven坐标:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.6.2</version>
<scope>test</scope>
</dependency>
- 在测试类中,使用注解方式完成
package top.open1024.spring;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
@ExtendWith(SpringExtension.class)
//加载配置文件
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class Spring5Test {
@Autowired
private Person person;
@Test
public void test(){
System.out.println(person);
}
}
5 WebFlux
5.1 Spring WebFlux介绍
-
Spring Webflux是Spring5添加的新的模块,用于web开发的,功能和SpringMVC类似,Webflux使用当前一种比较流行的响应式编程而出现的框架。
-
使用传统的web框架,比如Spring MVC,是基于Servlet容器;Webflux是一种异步非阻塞的框架,异步非阻塞是在Servlet3.1后才开始支持的,Webflux是基于Reactor的相关API实现的。
-
异步非阻塞:
-
异步和同步是针对调用者来说的。
-
当调用者发送请求,如果要等着对方回应之后才做其他的事情,这个现象称为同步。
-
当调用者发送请求,如果不需要等着对方回应之后就可以做其他的事情,这个现象称为异步。
-
-
阻塞和非阻塞是针对被调用者而言的。
-
当被调用者收到一个请求之后,做完请求任务之后才给出反馈就是阻塞。
-
当被调用者收到一个请求之后,马上给出反馈然后再去做请求任务就是非阻塞。
-
-
-
webflux的特点:
-
非阻塞式:在有限的资源下,提高系统吞吐量和伸缩性,以Reactor为基础实现响应式编程。
-
函数式编程:Spring5框架基于JDK8,webflux中可以使用JDK8函数式编程方式实现路由请求。
-
-
Spring MVC VS Spring Webflux
SpringMVC和SpringWebflux都可以使用注解方式,都运行在Tomcat等容器中。
SpringMVC采用命令式编程,SpringWebflux采用异步响应式编程。
5.2 响应式编程
5.2.1 什么是响应式编程?
-
响应式编程是一种面向数据流和变化传播的编程范式。这意味着可以在编程语言中很方便地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播。
-
电子表格程序就是响应式编程的一个例子。单元格可以包含字面值或类似"=B1+C1"的公式,而包含公式的单元格的值会依据其他单元格的值的变化而变化。
5.2.2 Java8及之前版本
-
提供了观察者模式的两个类Observer和Observable。
-
示例:
package top.open1024.spring.springbootspring5.reactor8;
import java.util.Observable;
public class ObserverDemo extends Observable {
public static void main(String[] args) {
ObserverDemo observerDemo = new ObserverDemo();
//添加观察者
observerDemo.addObserver((o, arg) -> {
System.out.println("发生了变化");
});
observerDemo.addObserver((o, arg) -> {
System.out.println("收到被观察者的通知,准备发生改变");
});
//数据变化
observerDemo.setChanged();
//通知
observerDemo.notifyObservers();
}
}
5.2.3 Reactor实现
-
响应式编程的操作中,Reactor是满足Reactive规范的一个框架,Webflux的核心是Reactor。
-
Reactor有两个核心类:Mono和Flux,这两个类都实现了接口Publisher,提供丰富的操作符。Flux对象实现发布者,返回N个元素;Mono对象实现发布者,返回0或1个元素。
-
Flux和Mono都是数据流的发布者,使用Flux和Mono都可以发送三种数据信号:元素值,错误信号,完成信号。错误信号和完成信号都代表终止信号,终止信号用来告诉订阅者数据流已经结束了,错误信号终止数据流同时把错误的信息传递给订阅者。
-
示例:演示Flux和Mono
-
导入reactor的jar包的Maven坐标:
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<version>3.1.5.RELEASE</version>
</dependency>
- ReactorTest.java
package top.open1024.spring.springbootspring5.reactor8;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
public class ReactorTest {
public static void main(String[] args) {
//just方法,直接声明
Flux.just(1,2,3,4);
Mono.just(1);
//其他方法
Integer[] arr = {1,2,3};
Flux.fromArray(arr);
List<Integer> list = Arrays.asList(1,2,3);
Flux.fromIterable(list);
Stream<Integer> stream = list.stream();
Flux.fromStream(stream);
}
}
- 三种信号的特点:
- 错误信号和完成信号都是终止信号,不能共存的。
- 如果没有发送任何元素值,而是直接发送错误或者完成信息,表示是一个空的数据流。
- 如果没有错误信号,也没有完成信号,表示是无限数据流。
- 调用just或者其他方法只是申明数据流,数据流并没有发出,只有进行订阅之后才会触发数据流,不订阅什么都不会发生的。
package top.open1024.spring.springbootspring5.reactor8;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
public class ReactorTest {
public static void main(String[] args) {
//just方法,直接声明
Flux.just(1,2,3,4).subscribe(System.out::println);
Mono.just(1).subscribe(System.out::println);
//其他方法
Integer[] arr = {1,2,3};
Flux.fromArray(arr).subscribe(System.out::println);
List<Integer> list = Arrays.asList(1,2,3);
Flux.fromIterable(list).subscribe(System.out::println);
Stream<Integer> stream = list.stream();
Flux.fromStream(stream).subscribe(System.out::println);
}
}
5.2.4 操作符
-
对数据流进行一道道的操作,称为操作符。比如工厂的流水线。
-
map:元素映射为新的元素。
- flatMap:元素映射为流。
5.3 WebFlux的执行流程和核心API
-
SpringWebFlux基于Reactor,默认使用的容器是Netty,Netty是高性能的NIO框架,异步非阻塞的框架。
-
BIO:
- NIO:
-
SpringWebFlux执行过程和SpringMVC执行过程类似。
-
SpringWebFlux核心控制器DispatcherHandler,实现了WebHandler接口。
-
WebHandler的源码如下:
-
package org.springframework.web.server;
import reactor.core.publisher.Mono;
public interface WebHandler {
Mono<Void> handle(ServerWebExchange var1);
}
-
- DispatcherHandler,实现了handle方法,其源码片段如下:
public class DispatcherHandler implements WebHandler, ApplicationContextAware {
//ServerWebExchange是HTTP的请求信息
public Mono<Void> handle(ServerWebExchange exchange) {
return this.handlerMappings == null ? this.createNotFoundError() : Flux.fromIterable(this.handlerMappings).concatMap((mapping) -> {
return mapping.getHandler(exchange);//根据请求地址获取对应的mapping
}).next().switchIfEmpty(this.createNotFoundError()).flatMap((handler) -> {
return this.invokeHandler(exchange, handler);//调用具体的业务方法
}).flatMap((result) -> {
return this.handleResult(exchange, result);//处理的结果返回
});
}
}
-
SpringWebFlux里面的DispatcherHandler,负责请求的处理。
-
HandlerMapping:请求查询到处理的方法。
-
HandlerAdapter:真正负责请求处理。
-
HandlerResultHandler:响应结果处理。
-
-
SpringWebFlux实现函数式编程,两个接口:RouterFunction(路由处理)和HandlerFunction(处理函数)。
5.4 SpringWebFlux(基于注解编程模型)
-
示例:以SpringBoot项目为例,使用SpringBoot2.x版本。
-
导入spring-boot-starter-webflux的jar包的Maven坐标:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
- User.java
package top.open1024.spring.springbootspring5.domain;
public class User {
private String username;
private String gender;
private Integer age;
public User() {
}
public User(String username, String gender, Integer age) {
this.username = username;
this.gender = gender;
this.age = age;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", gender='" + gender + '\'' +
", age=" + age +
'}';
}
}
- UserService.java
package top.open1024.spring.springbootspring5.service;
import top.open1024.spring.springbootspring5.domain.User;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
public interface UserService {
/**
* 根据id查询用户
*
* @param id
* @return
*/
Mono<User> findUserById(Integer id);
/**
* 查询所有用户
*
* @return
*/
Flux<User> findAllUser();
/**
* 添加用户
*
* @param userMono
* @return
*/
Mono<Void> saveUserInfo(Mono<User> userMono);
}
- UserServiceImpl.java
package top.open1024.spring.springbootspring5.service.impl;
import top.open1024.spring.springbootspring5.domain.User;
import top.open1024.spring.springbootspring5.service.UserService;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.HashMap;
import java.util.Map;
@Service
public class UserServiceImpl implements UserService {
private static Map<Integer, User> users = new HashMap<>();
static {
users.put(1, new User("lucy", "女", 20));
users.put(2, new User("marry", "女", 18));
users.put(3, new User("jack", "男", 45));
}
@Override
public Mono<User> findUserById(Integer id) {
return Mono.justOrEmpty(users.get(id));
}
@Override
public Flux<User> findAllUser() {
return Flux.fromIterable(users.values());
}
@Override
public Mono<Void> saveUserInfo(Mono<User> userMono) {
return userMono.doOnNext((user) -> {
Integer id = users.size() + 1;
users.put(id, user);
}).thenEmpty(Mono.empty());
}
}
- UserController.java
package top.open1024.spring.springbootspring5.web;
import top.open1024.spring.springbootspring5.domain.User;
import top.open1024.spring.springbootspring5.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@RestController
public class UserController {
@Autowired
private UserService userService;
/**
* 根据id查询用户信息
*
* @param id
* @return
*/
@GetMapping("/user/{id}")
public Mono<User> getUserId(@PathVariable("id") Integer id) {
return userService.findUserById(id);
}
/**
* 查询所有的用户信息
*
* @return
*/
@GetMapping("/users")
public Flux<User> findAllUser() {
return userService.findAllUser();
}
/**
* 新增用户信息
*
* @param user
* @return
*/
@PostMapping("/saveUser")
public Mono<Void> saveUser(@RequestBody User user) {
Mono<User> userMono = Mono.just(user);
return userService.saveUserInfo(userMono);
}
}
评论区