Spring5
Spring5 框架代码基于 Java8
1、通过使用泛型等特性提高可读性
2、对 Java8提高直接的代码支撑
3、运行时兼容 JDK9
4、Java EE 7 API 需要 Spring 相关模块支持
5、运行时兼容 Java EE 8 API
6、取消的包、类、方法
(1)包 beans.factory.access
(2)包 dbc.support.nativejdbc
7、从 spring-aspects 模块移除了包 mock.staicmock,不在支持 AnnotationDrivenStaticEntityMockingControl
8、在代码库中,删除不建议使用的类和方法
JDK8 增强
1、访问 Resouce 时提供 getFile、isFile 防御式抽象
2、有效的方法参数访问基于 Java8 反射增强
3、在 Spring 核心接口中,增加了声明 default 方法的支持,一贯使用 JDK7 Charset 和 Standardcharsets 的增强
4、兼容 JDK9
5、Spring5 框架自带通用的日志封装
6、持续实例化 via 构造函数,修改了异常处理
7、spring-jcl 替代通用的日志,仍然支持可重写
8、自动检测 log4j 2.x,SLF4J,JUL(java.util.Logging)
8、基于 NIO 的 readableChannel 也提供了这个新特性
核心容器
1、支持候选组件索引,也可以支持环境变量扫描)
2、支持 @Nullable 注解
3、函数式风格 GenericApplicationContext / AnnotationConfigApplicationContext
4、基本支持 bean API 注册
5、在接口层面使用 CGLIB 动态代理时,提供事物,缓存,异步注解检测
6、XML 配置作用域流式
7、Spring WebMVC
8、全部的 Servlet 3.1 签名支持在 Spring-provied Filter 实现
9、在 Spring MVC Controller 方法里支持 Servlet 4.0 PushBuilder 参数
10、多个不可变对象的数据绑定(Kotlin / Lombok / @ConstructorPorties)
11、支持 jackson 2.9
12、支持 JSON 绑定 API
13、支持 protobuf3
14、支持 Reactor 3.1 Flux、Mono
Spring5 框架自带通用的日志封装,已经移除 Log4jConfigListener,官方建议使用 Log4j2
1、依赖 jar 包
(1)log4j-api:log4j2 的 api 接口
(2)log4j-core:log4j2 的日志输出核心 jar 包
(3)log4j-slf4j-impl:slf4j 转接到 log4j2 的日志输出框架,不能和 log4j-to-slf4j 同时使用
(4)slf4j-api:slf4j 的 api 接口
2、创建 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>
Spring5 框架核心容器支持 @Nullable 注释
1、位置
(1)在方法上面,表示方法返回值可以为空
(2)在方法参数前,表示方法参数可以为空
(3)在属性上面,表示属性值可以为空
2、示例
//返回值允许为空
@Nullable
public void String getId();
//参数允许为空
public void registerBean(@Nullable String beanName){
}
//属性允许为空
@Nullable
private String bookName;
Spring5 核心容器支持函数式风格 GenericApplicationContext / AnnotationConfigApplicationContext
1、直接 new 的对象,Spring 无法管理,需要注册,才能被 Spring 管理
2、注册对象
public void registerGenericApplicationContext(){
//创建GenericApplicationContext对象
GenericApplicationContext genericApplicationContext = new GenericApplicationContext();
//调用方法注册对象
genericApplicationContext.refresh();
genericApplicationContext.registerBean(注册对象.class,()->new 类名());
//获取在Spring注册对象
类名 对象名 = (类名)context.getBean("全类名");
}
改进测试
1、完成对 JUnit 5's Juptier 编程和拓展模块在 Spring TestContext 框架
2、SpringExtension:是 JUnit 多个可拓展 API 的一个实现,提供对现存 Spring TestContext Framework 的支持,使用 @ExtendWith(SpringExtension.class) 注解引用
(1)@SpringJunitConfig:一个复合注解
(2)@ExtendWith(SpringExtension.class) 来源于 Junit Jupit
(3)@ContextConfiguration 来源于 Spring TestContext 框架
(4)@DisabledIf 如果提供的该属性值为 true 的表达或占位符,表示注解的测试类或测试方法被禁用
3、在 Spring TestContext 框架中支持并行测试
4、通过 SpringRunner 在 Sring TestContext 框架中支持 TestNG,Junit5,新的执行之前和之后测试回调
(1)在 testexecutionlistener API 和 testcontextmanager,beforetestexecution() 和 aftertestexecution() 回调
5、MockHttpServletRequest 新增 getContentAsByteArray()、getContentAsString() 来访问请求体
6、如果字符编码被设置为 mock 请求,在 print() 和 log() 中可以打印 Spring MVC Test 的 redirectedUrl() 和 forwardedUrl() 支持带变量表达式 URL 模板
7、XMLUnit 升级到 2.3 版本
Spring5 支持整合 JUnit5
1、引入 jar 包依赖
(1)spring-test
(2)junit-jupiter-api
2、@ExtendWith(SpringExtension.class)、@ContextConfiguration(示例)
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
@ExtendWith(SpringExtension.class)
@ContextConfiguration("classpath:文件名.xml")
public class Program {
@Test
public void test() {
}
}
3、@SpringJunitConfig(示例):简化 2
import org.junit.jupiter.api.Test;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
@SpringJUnitConfig(locations = "classpath:文件名.xml")
public class Program {
@Test
public void test() {
}
}
SpringWebFlux 概述
1、Spring5 添加新的模块,用于 Web 开发的,功能和 SpringMVC 类似,Webflux 使用响应式编程
2、SpringMVC 基于 Servlet 容器,Webflux 是一种异步非阻塞的框架,在 Servlet 3.1 之后支持异步非阻塞的框架,核心基于 Reactor 相关 API 实现
3、特点
(1)非阻塞式:在有限资源下,提高系统吞吐量和伸缩性,以 Reactor 为基础实现响应式编程
(2)函数式编程:Spring5 框架基于 Java8,Webflux 使用 Java8 函数式编程方式实现路由请求
4、SpringMVC、SpringWebFlux
(1)都可以使用注解,都运行在 Tomcat 等容器
(2)SpringMVC 采用命令式编程,同步阻塞,基于 SpringMVC + Servlet + Tomcat
(3)SpringWebFlux 采用响应式编程,异步非阻塞,基于 SpringWebflux + Reactor + Netty
5、基于 Reactor,默认使用容器 Netty,Netty 是高性能的 NIO(异步非阻塞)框架
6、执行过程类似 SpingMVC
7、WebHandler 接口
(1) 定义方法
public interface WebHandler {
Mono<Void> handle(ServerWebExchang var1)
}
(2)DispatchHandler 实现类,核心控制器,负责请求的处理
(3)实现方法
public Mono<Void> handle(ServerWebExchange exchange) {//传入http请求信息
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);//处理返回结果
});
}
(4)组件:HandlerMapping:请求查询到处理的方法;HandlerAdapter:真正负责请求处理;HandlerResultHandler:响应结果处理
(5)ResourceWebHandler 实现类,处理静态资源
(6)WebHandlerDecorator 实现类:装饰器
(7)RouterFunctionWebHandler 实现类:路由处理
8、实现函数式编程的两个接口:RouterFunction(路由处理)、HandlerFunction(处理函数)
9、实现方式
(1)注解编程模型:与 SpringMVC 使用相似,只需要把相关依赖配置到项目中,SpringBoot 自动配置相关运行容器,默认情况下使用 Netty 服务器
(2)函数式编程模型
响应式编程
1、RP:Reactive Programming
2、一种面向数据流和变化传播的编程范式,可以在编程语言中很方便地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播
3、观察者模式
(1)Java8 及其之前版本,提供两个类 Observer、Observable
(2)Java9 及其以后版本,提供 Flow 类:subcribe()、publish()
4、Reactor
(1)实现响应式编程
(2)满足 Reactive 规范的架构
(3)两个核心类:Mono、Flux,这两个类都实现 Publisher 接口,都是数据流发布者,提供了丰富的操作符
(4)Flux 返回 n 个元素;Mono 返回 0 或1 个元素
(5)使用 Flux 和 Mono 都可以发出三种数据信号:元素值,错误信号,完成信号
(6)错误信号、完成信号都代表终止信号,错误信号终止数据流时,同时把错误信息传递给订阅者
(7)示例
//在maven中引入依赖
<dependencies>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<version>3.4.18.RELEASE</version>
</dependency>
</dependencies>
//直接声明元素值
Flux.just(1,2,3,4);
Mono.just(1);
//声明数组
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);
(8)以上只是声明数据流,数据流并没有发出,只有调用 subscribe() 才会触发数据流
(9)错误信号和完成信号都是终止信号,不能共存
(10)如果没有发送任何信号元素值,而是直接发送错误 / 完成信号,表示空数据流
(11)如果没有错误信号,没有完成信号,表示无限数据流
(12)操作符,如:map:多个元素映射为新的多个元素;flapMap:多个元素映射(合并)为一个流
基于注解编程模型
1、创建 SpringBoot 工程,maven 引入依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
</dependencies>
2、properties 文件配置启动端口号:server.port=端口号
3、创建包和相关类(示例)
//实体类
public class User {
private String name;
private String gender;
private Integer age;
public User(String name, String gender, Integer age) {
this.name = name;
this.gender = gender;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
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;
}
}
public interface UserService {
//根据id查询用户
Mono<User> getUserById(int id);
//查询所有用户
Flux<User> getAllUser();
//添加用户
Mono<Void> saveUserInfo(Mono<User> user);
}
public class UserServiceImpl implements UserService {
//创建map集合存储数据
private final Map<Integer,User> userMap = new HashMap<>();
public UserServiceImpl() {
this.userMap.put(1, new User("lucy","nan",20));
this.userMap.put(2, new User("mary","nv",30));
this.userMap.put(3, new User("jack","nan",40));
this.userMap.put(4, new User("WeiSanJin","nan",50));
}
//根据id查询
@Override
public Mono<User> getUserById(int id) {
return Mono.justOrEmpty(this.userMap.get(id));
}
//查询多个用户
@Override
public Flux<User> getAllUser() {
return Flux.fromIterable(this.userMap.values());
}
//添加用户
@Override
public Mono<Void> saveUserInfo(Mono<User> userMono) {
return userMono.doOnNext(person -> {
/* 向map集合里面放值*/
int id = userMap.size()+1;
userMap.put(id,person);
}).thenEmpty(Mono.empty());//置空,表示完成信号
}
}
@RestController
public class UserController {
//注入service
@Autowired
private UserService userService;
//id查询
@GetMapping("/user/{id}")
public Mono<User> getUserId(@PathVariable int id){
return userService.getUserById(id);
}
//查询所有
@GetMapping("/user")
public Flux<User> getUser(){
return userService.getAllUser();
}
//添加
@PostMapping("/saveUser")
public Mono<Void> saveUser(@RequestBody User user){
Mono<User> userMono = Mono.just(user);
return userService.saveUserInfo(userMono);
}
}
基于函数式编程模型
1、使用函数式编程模型操作,需要手动初始化服务器
2、两个核心接口
(1)RouterFunction:实现路由功能,请求转发给对应的 handler
(2)HandlerFunction:处理请求生成响应的函数
3、定义两个函数式接口的实现并且启动需要的服务器
4、SpringWebflux 请求、响应不是 ServletRequest、ServletResponse,而是 ServerRequest、ServerResponse
5、示例
(1)保留注解的 entity、service
//实体类
public class User {
private String name;
private String gender;
private Integer age;
public User(String name, String gender, Integer age) {
this.name = name;
this.gender = gender;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
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;
}
}
public interface UserService {
//根据id查询用户
Mono<User> getUserById(int id);
//查询所有用户
Flux<User> getAllUser();
//添加用户
Mono<Void> saveUserInfo(Mono<User> user);
}
public class UserServiceImpl implements UserService {
//创建map集合存储数据
private final Map<Integer,User> userMap = new HashMap<>();
public UserServiceImpl() {
this.userMap.put(1, new User("lucy","nan",20));
this.userMap.put(2, new User("mary","nv",30));
this.userMap.put(3, new User("jack","nan",40));
this.userMap.put(4, new User("WeiSanJin","nan",50));
}
//根据id查询
@Override
public Mono<User> getUserById(int id) {
return Mono.justOrEmpty(this.userMap.get(id));
}
//查询多个用户
@Override
public Flux<User> getAllUser() {
return Flux.fromIterable(this.userMap.values());
}
//添加用户
@Override
public Mono<Void> saveUserInfo(Mono<User> userMono) {
return userMono.doOnNext(person -> {
/* 向map集合里面放值*/
int id = userMap.size()+1;
userMap.put(id,person);
}).thenEmpty(Mono.empty());//置空,表示完成信号
}
}
(2)创建 Handler(具体实现方法)
public class UserHandler {
private final UserService userService;
public UserHandler(UserService userService){
this.userService = userService;
}
//根据id查询
public Mono<ServerResponse> getUserById(ServerRequest request){
//获取id
int userId = Integer.valueOf(request.pathVariable("id"));
//空值处理
Mono<ServerResponse> notFound = ServerResponse.notFound().build();
//调用service方法得到数据
Mono<User> userMono = this.userService.getUserById(userId);
//userMono进行转换返回
//使用Reactor操作符fluxMap
return userMono
.flatMap(person -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromObject(person)))
.switchIfEmpty(notFound);
}
//查询所有
public Mono<ServerResponse> getAllUser(){
//调用service得到结果
Flux<User> userFlux = this.userService.getAllUser();
return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(userFlux,User.class);
}
//添加
public Mono<ServerResponse> saveUser(ServerRequest request){
//得到user对象
Mono<User> userMono = request.bodyToMono(User.class);
return ServerResponse.ok().build(this.userService.saveUserInfo(userMono));
}
}
(3)初始化服务器,编写 Router
public class Server {
//创建Router路由
public RouterFunction<ServerResponse> routerFunction(){
//创建hanler对象
UserService userService = new UserServiceImpl();
UserHandler handler = new UserHandler(userService);
//设置路由
return RouterFunctions.route(
GET("/user/{id}").and(accept(APPLICATION_JSON)),handler::getUserById)
.andRoute(GET("/user").and(accept(APPLICATION_JSON)),handler::getAllUser);
}
}
public class Server {
//创建服务器完成配置
public void createReactorServer(){
//路由和handler适配
RouterFunction<ServerResponse> route = routerFunction();
HttpHandler httpHandler = RouterFunctions.toHttpHandler(route);
ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler);
//创建服务器
HttpServer httpServer = HttpServer.create();
httpServer.handle(adapter).bindNow();
}
}
(4)使用 WebClient 调用
public class Client {
public static void main(String[] args) {
//调用服务器地址
WebClient webClient = WebClient.create("http://127.0.0.1:8080");
//根据ID查询
String id = "1";
User userResult = webClient.get().uri("/user/{id}",id)
.accept(MediaType.APPLICATION_JSON).retrieve().bodyToMono(User.class)
.block();
System.out.println(userResult.getName());
//查询所有
Flux<User> results = webClient.get().uri("/user").accept(MediaType.APPLICATION_JSON).retrieve().bodyToFlux(User.class);
results.map(stu ->stu.getName()).buffer().doOnNext(System.out::println).blockFirst();
}
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战