• Spring5框架学习笔记(四)


    1. Spring5 框架新功能(log、junit)

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

    2. Spring 5.0 框架自带了通用的日志封装

      • Spring5 已经移除 Log4jConfigListener,官方建议使用 Log4j2

      • Spring5 框架整合 Log4j2

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

      • 
        
        
        <configuration status="INFO">
          
          <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>
          
          
          <loggers>
            <root level="info">
              <appender-ref ref="Console"/>
            root>
          loggers>
        configuration>
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
        • 21
        • 22
        • 23
    3. Spring5 框架核心容器支持@Nullable 注解:

      • @Nullable 注解可以使用在方法上面,属性上面,参数上面,表示方法返回可以为空,属性值可以为空,参数值可以为空
      • 注解用在方法上面,方法返回值可以为空
      • 注解使用在方法参数里面,方法参数可以为空
      • 注解使用在属性上面,属性值可以为空
    4. Spring5 核心容器支持函数式风格 GenericApplicationContext:

      //函数式风格创建对象,交给 spring 进行管理
      @Test
      public void testGenericApplicationContext() {
       //1 创建 GenericApplicationContext 对象
       GenericApplicationContext context = new GenericApplicationContext();
       //2 调用 context 的方法对象注册
       context.refresh();
       context.registerBean("user1",User.class,() -> new User());
       //3 获取在 spring 注册的对象
       // User user = (User)context.getBean("com.atguigu.spring5.test.User");
       User user = (User)context.getBean("user1");
       System.out.println(user);
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
    5. Spring5 支持整合 JUnit4

      • 整合 JUnit4 第一步 引入 Spring 相关针对测试依赖

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

        @RunWith(SpringJUnit4ClassRunner.class) //单元测试框架
        @ContextConfiguration("classpath:bean1.xml") //加载配置文件
        public class JTest4 {
         @Autowired
         private UserService userService;
         @Test
         public void test1() {
         userService.accountMoney();
         } }
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
    6. Spring5 整合 JUnit5

      • 第一步 引入 JUnit5 的 jar 包

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

        @ExtendWith(SpringExtension.class)
        @ContextConfiguration("classpath:bean1.xml")
        public class JTest5 {
         @Autowired
         private UserService userService;
         @Test
         public void test1() {
         userService.accountMoney();
         } }
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
      • 使用一个复合注解替代上面两个注解完成整合

        @SpringJUnitConfig(locations = "classpath:bean1.xml")
        public class JTest5 {
         @Autowired
         private UserService userService;
         @Test
         public void test1() {
         userService.accountMoney();
         } }
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8

    2. Spring5 框架新功能(Webflux

    1. SpringWebflux 介绍

    1. 是 Spring5 添加新的模块,用于 web 开发的,功能和 SpringMVC 类似的,Webflux 使用当前一种比较流程响应式编程出现的框架。

      在这里插入图片描述

    2. 使用传统 web 框架,比如 SpringMVC,这些基于 Servlet 容器,Webflux 是一种异步非阻塞的框架,异步非阻塞的框架在 Servlet3.1 以后才支持,核心是基于 Reactor 的相关 API 实现的。

    3. 解释什么是异步非阻塞:针对对象不一样异步和同步

      • 异步和同步
      • 非阻塞和阻塞
      • 异步和同步针对调用者,调用者发送请求,如果等着对方回应之后才去做其他事情就是同步,如果发送请求之后不等着对方回应就去做其他事情就是异步
      • 阻塞和非阻塞针对被调用者,被调用者受到请求之后,做完请求任务之后才给出反馈就是阻塞,受到请求之后马上给出反馈然后再去做事情就是非阻塞
    4. Webflux 特点:

      • 第一 非阻塞式:在有限资源下,提高系统吞吐量和伸缩性,以 Reactor 为基础实现响应式编程
      • 第二 函数式编程:Spring5 框架基于 java8,Webflux 使用 Java8 函数式编程方式实现路由请求
    5. 比较 SpringMVC:

    在这里插入图片描述

    • 第一 两个框架都可以使用注解方式,都运行在 Tomcat 等容器中
    • 第二 SpringMVC 采用命令式编程,Webflux 采用异步响应式编程

    2. 响应式编程(Java 实现)

    1. 什么是响应式编程:响应式编程是一种面向数据流和变化传播的编程范式。这意味着可以在编程语言中很方便地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播。电子表格程序就是响应式编程的一个例子。单元格可以包含字面值或类似"=B1+C1"的公式,而包含公式的单元格的值会依据其他单元格的值的变化而变化。

    2. Java8 及其之前版本:提供的观察者模式两个类 Observer 和 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(); //通知
          }
        }
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15

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

    1. 响应式编程操作中,Reactor 是满足 Reactive 规范框架

    2. Reactor 有两个核心类,Mono 和 Flux,这两个类实现接口 Publisher,提供丰富操作符。Flux 对象实现发布者,返回 N 个元素;Mono 实现发布者,返回 0 或者 1 个元素

    3. Flux 和 Mono 都是数据流的发布者,使用 Flux 和 Mono 都可以发出三种数据信号:元素值,错误信号,完成信号,错误信号和完成信号都代表终止信号,终止信号用于告诉订阅者数据流结束了,错误信号终止数据流同时把错误信息传递给订阅者

      在这里插入图片描述

    4. 代码演示 Flux Mono

      • 第一步 引入依赖

      • 第二步 编程代码

        public static void main(String[] args) {
            //just方法直接声明
            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);
          }
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
      • 三种信号特点:

        • 错误信号和完成信号都是终止信号,不能共存的
        • 如果没有发送任何元素值,而是直接发送错误或者完成信号,表示是空数据流
        • 如果没有错误信号,没有完成信号,表示是无限数据流
    5. 调用 just 或者其他方法只是声明数据流,数据流并没有发出,只有进行订阅之后才会触发数据流,不订阅什么都不会发生的

    6. 操作符:对数据流进行一道道操作,成为操作符,比如工厂流水线

      • 第一 map 元素映射为新元素

        在这里插入图片描述

      • flatMap 元素映射为流,把每个元素转换流,把转换之后多个流合并大的流

        在这里插入图片描述

    7. SpringWebflux 执行流程和核心 API

      • SpringWebflux 基于 Reactor,默认使用容器是 Netty,Netty 是高性能的 NIO 框架,异步非阻塞的框架
      • BIO、NIO
    8. SpringWebflux 执行过程和 SpringMVC 相似的:

      • SpringWebflux 核心控制器 DispatchHandler,实现接口 WebHandler
      • 接口 WebHandler 有一个方法
      • SpringWebflux 里面 DispatcherHandler,负责请求的处理:HandlerMapping:请求查询到处理的方法、HandlerAdapter:真正负责请求处理、HandlerResultHandler:响应结果处理
      • SpringWebflux 实现函数式编程,两个接口:RouterFunction(路由处理)和 HandlerFunction(处理函数)

    4. SpringWebflux(基于注解编程模型)

    ​ SpringWebflux 实现方式有两种:注解编程模型和函数式编程模型,使用注解编程模型方式,和之前 SpringMVC 使用相似的,只需要把相关依赖配置到项目中,SpringBoot 自动配置相关运行容器,默认情况下使用 Netty 服务器

    1. 第一步 创建 SpringBoot 工程,引入 Webflux 依赖

    2. 第二步 配置启动端口号

    3. 第三步 创建包和相关类

      • 实体类

      • public class User {
          private String Name;
          private String Gender;
          private int Age;
        
          public User(String name, String gender, int age) {
            Name = name;
            Gender = gender;
            Age = age;
          }
        
          public String getName() {
            return Name;
          }
        
          public void setName(String name) {
            Name = name;
          }
        
          public String getGender() {
            return Gender;
          }
        
          public void setGender(String gender) {
            Gender = gender;
          }
        
          public int getAge() {
            return Age;
          }
        
          public void setAge(int age) {
            Age = age;
          }
        }
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
        • 21
        • 22
        • 23
        • 24
        • 25
        • 26
        • 27
        • 28
        • 29
        • 30
        • 31
        • 32
        • 33
        • 34
        • 35
    4. 创建接口定义操作的方法:

      • //用户操作接口
        public interface UserService {
          //根据id查询用户
          Mono<User> getUserById(int id);
          //查询所有用户
          Flux<User> getAllUser();
          //添加用户
          Mono<Void> saveUserInfo(Mono<User> user);
        }
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
      • 接口实现类

      • @Repository
        public class UserServiceImpl implements UserService{
        
          //创建map集合存储数据
          private final Map<Integer,User> users = new HashMap<>();
          public UserServiceImpl() {
            this.users.put(1,new User("lucy","nan",20));
            this.users.put(2,new User("mary","nv",30));
            this.users.put(3,new User("jack","nv",50));
          }
        
          //根据id查询
          @Override
          public Mono<User> getUserById(int id) {
            return Mono.justOrEmpty(this.users.get(id));
          }
        
          //查询多个用户
          @Override
          public Flux<User> getAllUser() {
            return Flux.fromIterable(this.users.values());
          }
        
          @Override
          public Mono<Void> saveUserInfo(Mono<User> user) {
            return user.doOnNext(person -> {
              //向 map 集合里面放值
              int id = users.size()+1;
              users.put(id,person);
            }).thenEmpty(Mono.empty());
          }
        }
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
        • 21
        • 22
        • 23
        • 24
        • 25
        • 26
        • 27
        • 28
        • 29
        • 30
        • 31
        • 32
    5. 创建 controller

      @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> getUsers() {
          return userService.getAllUser();
        }
        //添加
        @PostMapping("/saveuser")
        public Mono<Void> saveUser(@RequestBody User user) {
          Mono<User> userMono = Mono.just(user);
          return userService.saveUserInfo(userMono);
        }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
    6. 说明:

      • SpringMVC 方式实现,同步阻塞的方式,基于 SpringMVC+Servlet+Tomcat
      • SpringWebflux 方式实现,异步非阻塞 方式,基于 SpringWebflux+Reactor+Netty

    5. SpringWebflux(基于函数式编程模型)

    1. 在使用函数式编程模型操作时候,需要自己初始化服务器

    2. 基于函数式编程模型时候,有两个核心接口:RouterFunction(实现路由功能,请求转发给对应的 handler)和 HandlerFunction(处理请求生成响应的函数)。核心任务定义两个函数式接口的实现并且启动需要的服务器。

    3. SpringWebflux 请 求 和 响 应 不 再 是 ServletRequest 和 ServletResponse ,而是ServerRequest 和 ServerResponse

      • 第一步 把注解编程模型工程复制一份 ,保留 entity 和 service 内容

      • 第二步 创建 Handler(具体实现方法)

      • public class UserHandler {
          public 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操作符flatMap
            return userMono.flatMap(person->
                ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)
                    .bodyValue(fromObject(person))).switchIfEmpty(notFound);
          }
        
          //查询所有
          public Mono<ServerResponse> getAllUsers(ServerRequest request) {
            //调用service得到结果
            Flux<User> users = this.userService.getAllUser();
            return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(users,User.class);
          }
        
          //添加
          public Mono<ServerResponse> saveUser(ServerRequest request) {
            //得到user对象
            Mono<User> userMono = request.bodyToMono(User.class);
            return ServerResponse.ok().build(this.userService.saveUserInfo(userMono));
          }
        
        }
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
        • 21
        • 22
        • 23
        • 24
        • 25
        • 26
        • 27
        • 28
        • 29
        • 30
        • 31
        • 32
        • 33
        • 34
        • 35
        • 36
        • 37
      • 第三步 初始化服务器,编写 Router

        //1 创建Router路由
          public RouterFunction<ServerResponse> routingFunction() {
            //创建hanler对象
            UserService userService = new UserServiceImpl();
            UserHandler handler = new UserHandler(userService);
            //设置路由
            return RouterFunctions.route(GET("/users/{id}").and(accept(APPLICATION_JSON)),handler::getUserById)
                .andRoute(GET("/users").and(accept(APPLICATION_JSON)),handler::getAllUsers);
          }
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
      • 创建服务器完成适配

        //2 创建服务器完成适配
          public void createReactorServer() {
            //路由和handler适配
            RouterFunction<ServerResponse> route = routingFunction();
            HttpHandler httpHandler = toHttpHandler(route);
            ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler);
            //创建服务器
            HttpServer httpServer = HttpServer.create();
            httpServer.handle(adapter).bindNow();
          }
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
      • 最终调用

        public static void main(String[] args) throws Exception{
            Server server = new Server();
            server.createReactorServer();
            System.out.println("enter to exit");
            System.in.read();
          }
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
      1. 使用 WebClient 调用

        public class Client {
          public static void main(String[] args) {
            //调用服务器地址
            WebClient webClient = WebClient.create("http://127.0.0.1:54842");
            //根据 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.getName());
            //查询所有
            Flux<User> results = webClient.get().uri("/users").accept(MediaType.APPLICATION_JSON).retrieve().bodyToFlux(User.class);
            results.map(stu -> stu.getName()).buffer().doOnNext(System.out::println).blockFirst();
          } 
        }
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13

    3. 总结

    1. Spring 框架概述

      • 轻量级开源 JavaEE 框架,为了解决企业复杂性,两个核心组成:IOC 和 AOP
      • Spring5.2.6 版本
    2. IOC 容器

      • IOC 底层原理(工厂、反射等)
      • IOC 接口(BeanFactory)
      • IOC 操作 Bean 管理(基于 xml)
      • IOC 操作 Bean 管理(基于注解)
    3. Aop

      • AOP 底层原理:动态代理,有接口(JDK 动态代理),没有接口(CGLIB 动态代理)
      • 术语:切入点、增强(通知)、切面
      • 基于 AspectJ 实现 AOP 操作
    4. JdbcTemplate

      • 使用 JdbcTemplate 实现数据库 curd 操作
      • 使用 JdbcTemplate 实现数据库批量操作
    5. 事务管理

      • 事务概念
      • 重要概念(传播行为和隔离级别)
      • 基于注解实现声明式事务管理
      • 完全注解方式实现声明式事务管理
    6. Spring5 新功能

      • 整合日志框架
      • @Nullable 注解
      • 函数式注册对象
      • 整合 JUnit5 单元测试框架
      • SpringWebflux 使用
  • 相关阅读:
    TensorRT基础笔记
    HDU_1207
    皕杰报表之调整css样式
    ASEMI高压二极管CL08-RG210参数,CL08-RG210封装
    智能垃圾桶(七)——SG90舵机的介绍与使用(树莓派pico实现)
    STC 51单片机41——汇编 串口连续发送数据
    电脑风扇控制软件 Macs Fan Control Pro mac中文版功能介绍
    通过Python爬虫技术获取小说信息
    重学java基础----IO流
    司空见惯 - 世界人口变化
  • 原文地址:https://blog.csdn.net/weixin_42200347/article/details/126132206