目 录CONTENT

文章目录

spring之Spring5新特性(7)

Eric
2022-02-15 / 0 评论 / 0 点赞 / 189 阅读 / 3,342 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2023-12-12,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

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:元素映射为新的元素。

map操作符.jpg

  • flatMap:元素映射为流。

flatMap操作符.jpg

5.3 WebFlux的执行流程和核心API

  • SpringWebFlux基于Reactor,默认使用的容器是Netty,Netty是高性能的NIO框架,异步非阻塞的框架。

  • BIO:

BIO.jpg

  • NIO:

NIO.jpg

  • 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);
    }
}
0

评论区