Spring5框架新功能

116 阅读7分钟

1、整个Spring5框架的代码基于Java8实现,运行时兼容JDK9,许多不建议的类和方法在代码库中删除。

2、Spring5框架自带了通用的日志封装
(1)Spring5已经移除了Log4jConfigListener,官方建议使用Log4j2
(2)Spring5框架整合Log4j2
第一步 引入相关的jar包

image.png

第二步 创建log4j2.xml配置文件

3、Spring5框架核心容器支持@Nullable注解
(1)@Nullable注解可以使用在方法、属性、参数上面,分别表示方法返回为空、属性值可以为空、参数可以为空

4、支持函数式风格GenericApplicationContext

image.png

image.png

5、Spring5支持整合JUnit5
(1)整合Junit4
第一步 引入Spring针对测试的相关依赖

image.png

第二步 创建测试类,使用注解方式完成

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:bean1.xml")
public class JTest4 {

    @Autowired
    private UserService userService;

    @Test
    public void test() {
        userService.transfer();
    }


}

(2)Spring5整合Junit5
第一步 引入Junit5的jar包
第二步 创建测试类,使用Junit5注解完成

image.png 上述注解可以做一个简化,可以使用一个复合注解替代上面的两个注解

image.png 6、SpringWebFlux
前置知识:

image.png (1)基本介绍

  • 是Spring5添加新的模块,用于web开发的,功能和SpringMVC相似,但底层有很大区别。Webflux使用当前一种比较流行的响应式编程框架
  • 使用传统的web框架,比如SpringMVC,这些基于Servlet容器,Webflux是一种异步非阻塞的框架,这在servlet3.1之后才支持,核心是基于Reactor的相关API实现的
  • 解释什么是异步非阻塞
    • 异步和同步:针对调用者,调用者发送请求,如果等着对方回应后才做其他事情就是同步,如果发送请求之后不等着对方回应就去做其他的事情就是异步
    • 阻塞和非阻塞:针对被调用者,被调用者收到请求后,做完请求任务之后才给出反馈就是阻塞,如果收到请求之后马上给出反馈然后做事情就是非阻塞
  • Webflux特点:
    • 非阻塞式:在有限的资源下,提高系统的吞吐量和伸缩性,以Reactor为基础实现响应式编程
    • 函数式编程:Spring5是基于Java8的,所以Webflux使用Java8函数式编程实现路由请求
    • 比较SpringMVC:(1)都使用注解方式,都运行在Tomcat等容器中。(2)SpringMVC采用命令式编程,Webflux采用异步响应式编程。

image.png (2)响应式编程

  • 什么是响应式编程:电子表格程序就是响应式编程的一个例子,单元格可以包含字面值或类似于=“B1+C1”的公式,而包含公式的单元格的值会依据其他单元格的值的变化而变化
  • Java8及其之前版本提供了观察者模式的两个类Observer和Observable
public class ObserverDemo extends Observable {
    public static void main(String[] args) {
        ObserverDemo demo = new ObserverDemo();
        demo.addObserver((o,arg)->{
            System.out.println("发生变化");
        });
        demo.addObserver((o,arg)->{
            System.out.println("手动被观察者通知,准备改变");
        });
        demo.setChanged();
        demo.notifyObservers();
    }
}

(3)响应式编程(Reactor实现)

  • 响应式编程操作中,Reactor是满足Reactive规范框架
  • Reactor有两个核心类,Mono和Flux,这两个类实现接口Publisher,提供丰富操作符。Flux是对象实现发布者,返回N个元素;Mono实现发布者,返回0或者1个元素。
  • Flux和Mono都是数据流的发布者,使用Flux和Mono都可以发出三种数据信号:元素值、错误信号和完成信号。错误信号和完成信号都代表终止信号,终止信号用于告诉订阅者数据流结束了,错误信号终止数据流的同时把错误信息传递给订阅者。
  • 代码演示Flux和Mono 第一步 引入依赖

image.png 第二步 编写代码

public class Fluxtest {
    public static void main(String[] args) {
        //just方法直接声明
        Flux.just(1,2,3,4).subscribe(System.out::print);
        Mono.just(1).subscribe(System.out::print);

        //其他方法
        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);

    }
}
  • 三种信号特点
    • 错误信号和完成信号都是终止信号,不能共存
    • 没有发送任何元素值,而是直接发送错误或者完成信号,表示是空数据流。
    • 没有错误信号和完成信号,表示无限数据流
  • 调用just或者其他方法,只是声明了数据流,数据流并没有发出,只有进行订阅后才会触发数据流。
  • 操作符
    • 对数据流进行一道道操作,称为操作符,比如工厂流水线
    • 常见操作符:(1)map 元素映射为新的元素(2)flatmap 把元素映射为流(把每个元素变成流,再把多个流变成一个大流)

image.png

image.png (4)Webflux执行流程和核心API
SpringWebflux基于Reactor,默认使用容器事Netty,Netty是高性能的NIO框架,即异步非阻塞框架

  • Netty
    • BIO(阻塞方式) image.png
    • NIO(非阻塞方式)

image.png

  • SpringWebflux执行过程和SpringMVC是相似的
    • 核心控制器:DispatchHandler,实现接口WebHandler
    • 接口WebHandler里有个方法handle

image.png

image.png

  • DispatcherHandler主要负责请求的处理
    • HandlerMapping:请求查询到处理的方法
    • HandlerAdapter:真正负责请求处理
    • HandlerResultHandler:相应结果处理
  • SpringWebflux实现函数式编程,两个接口:RouterFunction(路由处理)和HandlerFunction(处理函数) (5)SpringWebflux(基于注解编程模型)
    SpringWebflux实现方式有两种:注解编程模型和函数式编程模型
    使用注解编程模型方式,和之前的SpringMVC使用相似,只需要把相关依赖配置到项目中,SpringBoot自动配置相关运行容器,默认情况下使用Netty服务器
  • 创建SpringBoot工程,引入webflux依赖

image.png

  • 配置启动的端口号
  • 创建包和相关类
    • 实体类
    public class User {
    private String username;
    private String usersex;
    
    public User(String username, String usersex) {
        this.username = username;
        this.usersex = usersex;
    }
    
    public String getUsername() {
        return username;
    }
    
    public void setUsername(String username) {
        this.username = username;
    }
    
    public String getUsersex() {
        return usersex;
    }
    
    public void setUsersex(String usersex) {
        this.usersex = usersex;
    }
    }  
    
    • 创建接口定义相关的方法
    public interface UserService {
        public Mono<User> getUserById(int id);
        public Flux<User> getAllUsers();
        public Mono<Void> saveUserInfo(Mono<User> user);
    }
    
    • 实现相关的方法
    @Service
    public class UserServiceImpl implements UserService{
    
        private final Map<Integer,User> userMap = new HashMap<>();
    
        public UserServiceImpl() {
            this.userMap.put(1,new User("Json","man"));
            this.userMap.put(2,new User("Marry","woman"));
            this.userMap.put(3,new User("Harden","man"));
    
        }
    
        @Override
        public Mono<User> getUserById(int id) {
            return Mono.justOrEmpty(this.userMap.get(id));
        }
    
        @Override
        public Flux<User> getAllUsers() {
            return Flux.fromIterable(this.userMap.values());
        }
    
        @Override
        public Mono<Void> saveUserInfo(Mono<User> user) {
            return user.doOnNext(person->{
                int id = this.userMap.size() + 1;
                userMap.put(id, person);
            }).thenEmpty(Mono.empty());
        }
    }
    
    • 创建controller
    @RestController
    public class UserController {
    
        @Autowired
        private UserService userService;
    
        @GetMapping("/user/{id}")
        Mono<User> getUserById(@PathVariable int id){
            return userService.getUserById(id);
        }
    
        @GetMapping("/user")
        Flux<User> getAllUsers(){
            return userService.getAllUsers();
        }
        @GetMapping("/user/add")
        Mono<Void> saveUserInfo(@RequestBody User user){
            return userService.saveUserInfo(Mono.just(user));
        }
    }
    
  • 说明:SpringMVC方式实现,同步阻塞的方式,基于SpringMVC+Servlet+Tomcat。SpringWebflux方式实现,异步非阻塞方式,基于SprngWebflux+Reactor+Netty实现 (6)SpringWebflux(基于函数式编程模型)
  • 在使用函数式编程模型操作的时候,需要自己初始化服务器
  • 基于函数式编程模型的时候,有两个核心接口:RouterFounction(实现路由功能,请求转发给对应的handler)和HandlerFunction(处理请求生成响应的函数)。核心任务定义:两个函数式接口的实现并且启动需要的服务器
  • SpringWebflux请求和响应不再是ServletRequest和ServletResponse,而是ServerRequest和ServerResponse
  • 编程步骤
    • 把注解编程模型的工程复制一份
    • 创建Handler(具体实现方法)
    public class UserHandler {
        private UserService userService;
        public UserHandler(UserService userService){
            this.userService = userService;
        }
    
        public Mono<ServerResponse> getUserById(ServerRequest request){
            int id = Integer.valueOf(request.pathVariable("id"));
            Mono<ServerResponse> notfound = ServerResponse.notFound().build();
            Mono<User> User = userService.getUserById(id);
            return User.flatMap(person -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(fromObject(person)).switchIfEmpty(notfound));
        }
    
        public Mono<ServerResponse> getAllUsers(ServerRequest request){
            Flux<User> user = userService.getAllUsers();
            return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(user,User.class);
        }
        public Mono<ServerResponse> saveUserInfo(ServerRequest request){
            Mono<User> user = request.bodyToMono(User.class);
            return ServerResponse.ok().build(this.userService.saveUserInfo(user));
        }
    }
    
    • 初始化服务器,编写Router
    //创建Router路由
    public RouterFunction<ServerResponse> routingFunction(){
        //创建handle对象
        UserService userService = new UserServiceImpl();
        UserHandler handler = new UserHandler(userService);
        //设置路由
        return RouterFunctions.route(
                GET("/users/{id}").and(accept(APPLICATION_JSON)), handler::getUserById)
                        .andRoute(GET("/user").and(accept(APPLICATION_JSON)), handler::getAllUsers);
    }
    
    • 创建服务器,完成适配
    //创建服务器完成适配
    public void createReactorServer() throws IOException {
        //路由和handler适配
        RouterFunction<ServerResponse> route = routingFunction();
        HttpHandler httpHandler = toHttpHandler(route);
        ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler);
    
        //创建服务器
        HttpServer httpServer = HttpServer.create();
        httpServer.handle(adapter).bindNow();
    }
    
    • 最终调用
    public static void main(String[] args) throws IOException {
        Server server = new Server();
        server.createReactorServer();
        System.out.println("enter to exit");
        System.in.read();
    }
    
    • 使用WebClient调用
    public class Client {
        public static void main(String[] args) {
            //调用服务器地址
            WebClient webClient = WebClient.create("http://127.0.0.1:5794");
    
            //根据id查询
            String id = "1";
            User userresult = webClient.get().uri("/users/{id}",id)
                    .accept(MediaType.APPLICATION_JSON).retrieve().bodyToMono(User.class)
                    .block();
            System.out.println(userresult.getUsername());
        }
    
    }