-
Spring5自带的通用日志框架
-
移除了Log4JConfigListener
-
整合了Log4J2
-
-
支持@Nullable注解,用于方法、属性、参数上
-
用在方法上,方法返回值可以为空
@Nullable public String getId(){}
-
用在属性上,属性值可以为空
@Nullable private String name;
-
用在方法参数前,方法参数可以为空
public void show(@Nullable String name,String id){}
-
-
支持函数式风格GengericApplicationContext/AnnotationConfigApplicationContext
GenericApplicationContext context = new GenericApplicationContext(); //调用context的方法对象注册 context.refresh(); //获取bean不再是首字母小写 // 两种方式 // 1.在registerBean中的beanName参数中传入当前bean的名称 //context.registerBean("user",User.class,()->new User()); //User user = (User)context.getBean("user"); //2.使用类的全路径名 context.registerBean(User.class,()->new User()); User user = (User)context.getBean("com.carl.entity.User"); System.out.println(user); //AnnotationConfigApplicationContext使用方法一样
-
测试方面的改进
-
完成了对Junit5 Juptier编程和拓展模块
-
SpringExtension是JUnit多个可扩展API的一个实现,提供了对现存的Spring TestContext框架的支持
-
@ExtendWith注解
-
@SpringJunitConfig复合注解
-
@ContextConfiguration来源于Spring TextContext框架
-
@DisabledIf:该注解如果属性值为true,则测试类或测试方法将被禁用
-
-
在Spring TestContext框架中支持并行测试
-
在testexecutionlistener API和testcontextmanager新的beforetestexectuion和aftertestexecution回调
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = SpringConfig.class)//完全注解开发 //@ContextConfiguration("classpath:/src/bean1.xml")//配置文件开发 public class Test4 { @Autowired private UserService userService; @Test public void test1(){ User user = userService.getUserById("051bc7fc-c61e-4e35-8843-b28ce8dfc24f"); System.out.println(user); } }
-
Spring WEBflux
1.什么是Webflux?
Spring WebFlux是用于对标SpringMVC的,用于Web开发的响应式编程框架
新的Spring-webflux模块,一个基于
reactive stream
(反应流)的spring-webmvc
,完全的异步非阻塞,目的是在使用enent-loop(事件循环机制)执行模型和传统的线程池模型,支持在Netty
,Undertow
和Servlet3.1以上容器
等服务器上运行
同步和异步:异步和同步针对调用者
-
同步:调用者发送请求,如果等着对方回应,才去做其他事情就是同步
-
异步:调用者发送请求,在等待对方回应的同时在做其他的事情就是异步
阻塞和非阻塞:阻塞和非阻塞针对被调用者
-
阻塞:被调用者收到请求之后,做完请求任务才给出反馈就是阻塞
-
非阻塞:被调用者收到请求之后,马上给出反馈然后再做请求任务就是非阻塞
2.Webflux的特点
非阻塞式:在有线资源下,提高系统吞吐量和伸缩性,以Reactor为基础实现响应式编程
函数式编程:Spring5框架基于java8,Webflux使用java8函数式编程方式实现路由请求
Spring WebFlux区别于SpringMVC的特点:
-
Spring WebFlux是异步非阻塞IO模型,通过少量容器线程就可以支持大量的并发访问
-
Spring WebFlux底层使用的是Netty容器,SpringMVC使用的是Servlet
-
Spring WebFlux采用的是异步响应式编程,SpringMVC采用的是命令式编程
-
Spring WebFlux方式实现异步非阻塞方式是基于SpringWebFlux+Reactor+Netty
Spring MVC方式实现同步阻塞方式是基于Spring+Servlet+Tomcat
3.响应式编程(重点)
什么是响应式编程?
响应式编程是一种面向数据流和变化传播的编程范式,用于便捷的表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播
例如:excel中的B1=5;C1=6;D1=B1+C1;C1变成2,D1也会从11对应的变化为7
Java8提供的观察者模式的两个ObServer和ObServable类,通过这两个类基本实现响应式编程
public class ObserverDemo extends Observable {
public static void main(String[] args) {
ObserverDemo observer = new ObserverDemo();
observer.addObserver((o,arg) -> System.out.println("发生变化"));
observer.addObserver((o,arg) -> System.out.println("收到被观察者通知,准备改变"));
//监控数据是否变化
observer.setChanged();
//通知
observer.notifyObservers();
}
}
Reactor实现响应式编程
响应式编程操作中,Reator是满足Reative规范框架
Reator有两个核心类,Mono和Flux,这两个类实现接口Publisher,提供了丰富操作符
-
Flux对象实现发布者,返回N个元素
-
Mono实现发布者,返回0/1个元素
Flux和Mono都是数据流的发布者,使用Flux和Mono都可以发出三种数据信号
-
错误信号
-
完成信号
-
元素值
完成信号和错误信号都代表终止信号,终止信号用于告诉订阅者:数据流结束了
完成信号和错误信号不能共存,如果没有终止信号,表示是无限数据流
实现Reactor编程步骤
第一步:导入依赖
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<version>3.4.24</version>
</dependency>
第二步:使用Flux注入数据
//just方法直接注入数据
Flux.just(1,2,3,4);
//其他方法
Integer[] array={1,2,3,4};
Flux.fromArray(array);
List<Integer> list = Arrays.asList(array);
Flux.fromIterable(list);
Stream<Integer> stream = list.stream();
Flux.fromStream(stream);
Mono.just(1);
第三步:订阅(注入数据不会发出任何数据流,只有定于才会触发)
//just方法直接声明数据
Flux.just(1,2,3,4).subscribe(System.out::println);
//其他方法
Integer[] array={1,2,3,4};
Flux.fromArray(array).subscribe(System.out::println);
List<Integer> list = Arrays.asList(array);
Flux.fromIterable(list).subscribe(System.out::println);
Stream<Integer> stream = list.stream();
Flux.fromStream(stream).subscribe(System.out::println);
Mono.just(1).subscribe(System.out::println);
第四步:操作符(对数据流进行的一道道操作,称为操作符)
map:将元素映射为新的元素
flatMap:把每个元素转换成流,把转换之后的多个流合并成一个流
4.WebFlux的执行流程
Spring-WebFlux和SpringMVC执行流程基本相似
SpringWebFlux核心控制器DispatchHandler,实现接口WebHandler
public interface WebHandle{
//ServerWebExchange是Http请求响应信息
Mono<void> handle(ServerWebExchange exchange);
//DispatchHandler主要实现是
//1.根据请求地址获取对应的mapping参数
//2.调用具体的业务方法
//3.处理结果返回
}
DispatchHandler中负责请求的处理器
-
HandleMapping:请求查询到处理的方法
-
HandleAdapter:真正负责请求处理
-
HandleResultHandle:响应结果处理
SpringWebFlux实现函数式编程的两个接口:RouterFunction(路由处理接口)和HandleFunction(方法处理接口)
ResourceWebHandle:静态资源处理器
注解式编程模型
-
创建Springboot项目
-
添加依赖
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.14.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>Demo8</artifactId> <version>0.0.1-SNAPSHOT</version> <name>Demo8</name> <description>Demo8</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!--千万不要在这里限制版本,要统一版本,否则会报错NoSuchEror--> <dependency> <groupId>io.projectreactor</groupId> <artifactId>reactor-core</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <!--spring-boot-maven-plugin报红原因是Springboot-parent版本号没有和下面的版本号对应--> <version>2.1.14.RELEASE</version> </plugin> </plugins> </build> </project>
-
在配置文件中添加端口号
-
定义User实体类
-
编写user的业务层
public interface UserService { /** * 根据id查询用户 * @param id 身份证件 * @return {@link Mono }<{@link User }> */ Mono<User> getUser(int id); /** * 查询所有用户 * @return {@link Flux }<{@link User }> */ Flux<User> getAllUsers(); /** * 添加用户 * @author carl * @param user 用户信息 * @return Mono<Void> * @updateRemark: [本次修改内容] */ Mono<Void> saveUserInfo(Mono<User> user); }
-
实现UserSerivce接口
@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","女",60)); } @Override public Mono<User> getUser(int id) { return Mono.justOrEmpty(this.users.get(id)); } @Override public Flux<User> getAllUsers() { return Flux.fromIterable(this.users.values()); } @Override public Mono<Void> saveUserInfo(Mono<User> userMono) { return userMono.doOnNext(person->{ //向map中存入值 int id = users.size() + 1; users.put(id,person); }).thenEmpty(Mono.empty()); } }
-
实现Controller
@RestController public class UserController { @Autowired private UserService userService; /** * id查询 * @author carl * @param id user的主键 * @return 返回结果 * @updateRemark: [本次修改内容] */ @GetMapping("/{id}") public Mono<User> getUserById(@PathVariable int id) { return userService.getUser(id); } /** * 查询所有用户信息 * @return {@link Flux }<{@link User }> */ @GetMapping("/user") public Flux<User> getUsers(){ return userService.getAllUsers(); } /** * 保存用户 * @param user 用户信息 * @return {@link Mono }<{@link Void }> */ @PostMapping("/save") public Mono<Void> saveUser(@RequestBody User user) { Mono<User> userMono=Mono.just(user); return userService.saveUserInfo(userMono); } }
此时启动SpringBoot,访问http://localhost:8081/1
http://localhost:8081/user
函数式编程模型
使用函数式编程模型,和注解式编程模型的区别在于,函数式编程模型操作时需要自己初始化服务器
基于函数式编程模型有两个核心接口:
-
RouterFunction:实现路由功能,请求转发给对应的Handler
-
HandleFunction:处理请求生成 响应的函数
核心任务时定义两个函数式接口的实现,并启动需要的服务器
SpringWenFlux请求和响应不再是ServletRequest和ServletResponse,而是ServerRequest和ServerResponse
注解式编程步骤中的前六步都不需要改变
在上面六步的基础上实现以下步骤
-
实现Handler
package com.carl.demo8.handler; import com.carl.demo8.entity.User; import com.carl.demo8.service.UserService; 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; /** * @PackageName com.carl.demo8 * @author carl * @Description: TODO 类描述 * @Version V1.0 * @Date 2022-11-30 1:57 * Modified By:TODO **/ public class UserHandler { private final UserService userService; public UserHandler(UserService userService) { this.userService = userService; } /** * 按id获取用户 * @param request 请求参数 * @return {@link Mono }<{@link ServerResponse }> */ public Mono<ServerResponse> getUserById(ServerRequest request){ int id = Integer.parseInt(request.pathVariable("id")); //非空判断 Mono<ServerResponse> response = ServerResponse.notFound().build(); Mono<User> user = this.userService.getUser(id); //使用Reactor操作符flatMap,将对象以流的形式返回,如果对象为空,则返回一个空对象流 return user.flatMap(person->ServerResponse.ok() .contentType(MediaType.APPLICATION_JSON) .body(fromObject(person))).switchIfEmpty(response); } /** * 获取所有用户 * @param request 请求参数 * @return {@link Mono }<{@link ServerResponse }> */ public Mono<ServerResponse> getAllUsers(ServerRequest request){ Flux<User> users= this.userService.getAllUsers(); return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(users,User.class); } /** * 保存用户 * @param request 请求参数 * @return {@link Mono }<{@link ServerResponse }> */ public Mono<ServerResponse> saveUser(ServerRequest request){ Mono<User> userMono = request.bodyToMono(User.class); return ServerResponse.ok().build(this.userService.saveUserInfo(userMono)); } }
-
初始化服务器,编写Router
package com.carl.demo8.controller; import com.carl.demo8.handler.UserHandler; import com.carl.demo8.service.UserService; import com.carl.demo8.service.impl.UserServiceImpl; 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 static org.springframework.http.MediaType.APPLICATION_JSON; import static org.springframework.web.reactive.function.server.RequestPredicates.*; /** * @PackageName com.carl.demo8.controller * @author carl * @Description: TODO 类描述 * @Version V1.0 * @Date 2022-11-30 2:13 * Modified By:TODO **/ public class Server { public RouterFunction<ServerResponse> routerFunction(){ //创建UserHandler UserService userService = new UserServiceImpl(); UserHandler userHandler = new UserHandler(userService); //关联UserHandler return RouterFunctions.route(GET("/user/{id}").and(accept(APPLICATION_JSON)),userHandler::getUserById) .andRoute(GET("/users").and(accept(APPLICATION_JSON)),userHandler::getAllUsers) .andRoute(POST("/save").and(accept(APPLICATION_JSON)),userHandler::saveUser); } }
-
在Server 类中创建服务器完成适配
public void createReactorServer(){ //路由和Handler适配 RouterFunction<ServerResponse> route=routerFunction(); HttpHandler httpHandler = toHttpHandler(route); ReactorHttpHandlerAdapter reactorHttpHandlerAdapter = new ReactorHttpHandlerAdapter(httpHandler); //创建服务器 HttpServer httpServer = HttpServer.create(); httpServer.handle(reactorHttpHandlerAdapter).bindNow(); }
-
编写执行线程
public static void main(String[] args) throws IOException { Server server=new Server(); server.createReactorServer(); System.out.println("enter to exit"); System.in.read(); }
-
运行端口号在日志中打印
此时访问http://localhost:60739/user/1
http://localhost:60739/users
5.SpringWebFlux的Client调用
package com.carl.demo8.client;
import com.carl.demo8.entity.User;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
/**
* @PackageName com.carl.demo8.client
* @author carl
* @Description: TODO 类描述
* @Version V1.0
* @Date 2022-11-30 2:34
* Modified By:TODO
**/
public class Client {
public static void main(String[] args){
WebClient webClient = WebClient.create("http://127.0.0.1:55788");
//调用根据id查询user
int id = 1;
User user=webClient.get().uri("/user/{id}",id)
.retrieve().bodyToMono(User.class).block();
System.out.println(user.getName());
//查询所有
Flux<User> userFlux = webClient.get().uri("/users").accept(MediaType.APPLICATION_JSON).retrieve().bodyToFlux(User.class);
userFlux.map(users->users.getName()).buffer().doOnNext(System.out::println).blockFirst();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>tech.pdai</groupId>
<artifactId>001-spring-framework-demo-helloworld-xml</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<spring.version>5.3.9</spring.version>
<aspectjweaver.version>1.9.6</aspectjweaver.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${aspectjweaver.version}</version>
</dependency>
</dependencies>
</project>