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();
    }
}
posted @   半条咸鱼  阅读(117)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示