Spring5新功能

  1. Spring5自带的通用日志框架

    • 移除了Log4JConfigListener

    • 整合了Log4J2

  2. 支持@Nullable注解,用于方法、属性、参数上

    • 用在方法上,方法返回值可以为空

      @Nullable
      public String getId(){}
      
    • 用在属性上,属性值可以为空

      @Nullable
      private String name;
      
    • 用在方法参数前,方法参数可以为空

      public void show(@Nullable String name,String id){}
      
  3. 支持函数式风格GengericApplicationContext/AnnotationConfigApplicationContext

    GenericApplicationContext context = new GenericApplicationContext();
    //调用context的方法对象注册
    context.refresh();
    //获取bean不再是首字母小写
    // 两种方式
    // 1.在registerBean中的beanName参数中传入当前bean的名称
    //context.registerBean("user",User.class,()->new User());
    //User user = (User)context.getBean("user");
    //2.使用类的全路径名
    context.registerBean(User.class,()->new User());
    User user = (User)context.getBean("com.carl.entity.User");
    System.out.println(user);
    //AnnotationConfigApplicationContext使用方法一样
    
  4. 测试方面的改进

    • 完成了对Junit5 Juptier编程和拓展模块

      • SpringExtension是JUnit多个可扩展API的一个实现,提供了对现存的Spring TestContext框架的支持

      • @ExtendWith注解

      • @SpringJunitConfig复合注解

      • @ContextConfiguration来源于Spring TextContext框架

      • @DisabledIf:该注解如果属性值为true,则测试类或测试方法将被禁用

    • 在Spring TestContext框架中支持并行测试

    • 在testexecutionlistener API和testcontextmanager新的beforetestexectuion和aftertestexecution回调

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = SpringConfig.class)//完全注解开发
    //@ContextConfiguration("classpath:/src/bean1.xml")//配置文件开发
    public class Test4 {
        @Autowired
        private UserService userService;
        @Test
        public void test1(){
            User user = userService.getUserById("051bc7fc-c61e-4e35-8843-b28ce8dfc24f");
            System.out.println(user);
        }
    }
    

Spring WEBflux

1.什么是Webflux?

Spring WebFlux是用于对标SpringMVC的,用于Web开发的响应式编程框架

新的Spring-webflux模块,一个基于reactive stream(反应流)的spring-webmvc,完全的异步非阻塞,目的是在使用enent-loop(事件循环机制)执行模型和传统的线程池模型,支持在Netty,UndertowServlet3.1以上容器等服务器上运行

同步和异步:异步和同步针对调用者

  • 同步:调用者发送请求,如果等着对方回应,才去做其他事情就是同步

  • 异步:调用者发送请求,在等待对方回应的同时在做其他的事情就是异步

阻塞和非阻塞:阻塞和非阻塞针对被调用者

  • 阻塞:被调用者收到请求之后,做完请求任务才给出反馈就是阻塞

  • 非阻塞:被调用者收到请求之后,马上给出反馈然后再做请求任务就是非阻塞

2.Webflux的特点

非阻塞式:在有线资源下,提高系统吞吐量和伸缩性,以Reactor为基础实现响应式编程

函数式编程:Spring5框架基于java8,Webflux使用java8函数式编程方式实现路由请求

在这里插入图片描述

Spring WebFlux区别于SpringMVC的特点:

  1. Spring WebFlux是异步非阻塞IO模型,通过少量容器线程就可以支持大量的并发访问

  2. Spring WebFlux底层使用的是Netty容器,SpringMVC使用的是Servlet

  3. Spring WebFlux采用的是异步响应式编程,SpringMVC采用的是命令式编程

  4. Spring WebFlux方式实现异步非阻塞方式是基于SpringWebFlux+Reactor+Netty

Spring MVC方式实现同步阻塞方式是基于Spring+Servlet+Tomcat

3.响应式编程(重点)

什么是响应式编程?

响应式编程是一种面向数据流和变化传播的编程范式,用于便捷的表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播

例如:excel中的B1=5;C1=6;D1=B1+C1;C1变成2,D1也会从11对应的变化为7

Java8提供的观察者模式的两个ObServer和ObServable类,通过这两个类基本实现响应式编程

public class ObserverDemo extends Observable {
    public static void main(String[] args) {
        ObserverDemo observer = new ObserverDemo();
        observer.addObserver((o,arg) -> System.out.println("发生变化"));
        observer.addObserver((o,arg) -> System.out.println("收到被观察者通知,准备改变"));
        //监控数据是否变化
        observer.setChanged();
        //通知
        observer.notifyObservers();
    }
}

Reactor实现响应式编程

响应式编程操作中,Reator是满足Reative规范框架

Reator有两个核心类,Mono和Flux,这两个类实现接口Publisher,提供了丰富操作符

  • Flux对象实现发布者,返回N个元素

  • Mono实现发布者,返回0/1个元素

Flux和Mono都是数据流的发布者,使用Flux和Mono都可以发出三种数据信号

  1. 错误信号

  2. 完成信号

  3. 元素值

完成信号和错误信号都代表终止信号,终止信号用于告诉订阅者:数据流结束了

完成信号和错误信号不能共存,如果没有终止信号,表示是无限数据流

实现Reactor编程步骤

第一步:导入依赖

<dependency>
    <groupId>io.projectreactor</groupId>
    <artifactId>reactor-core</artifactId>
    <version>3.4.24</version>
</dependency>

第二步:使用Flux注入数据

//just方法直接注入数据
Flux.just(1,2,3,4);
//其他方法
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);

Mono.just(1);

第三步:订阅(注入数据不会发出任何数据流,只有定于才会触发)

//just方法直接声明数据
Flux.just(1,2,3,4).subscribe(System.out::println);
//其他方法
Integer[] array={1,2,3,4};
Flux.fromArray(array).subscribe(System.out::println);
List<Integer> list = Arrays.asList(array);
Flux.fromIterable(list).subscribe(System.out::println);
Stream<Integer> stream = list.stream();
Flux.fromStream(stream).subscribe(System.out::println);

Mono.just(1).subscribe(System.out::println);

第四步:操作符(对数据流进行的一道道操作,称为操作符)

map:将元素映射为新的元素

flatMap:把每个元素转换成流,把转换之后的多个流合并成一个流

4.WebFlux的执行流程

Spring-WebFlux和SpringMVC执行流程基本相似

SpringWebFlux核心控制器DispatchHandler,实现接口WebHandler

public interface WebHandle{
  //ServerWebExchange是Http请求响应信息
  Mono<void> handle(ServerWebExchange exchange);
  //DispatchHandler主要实现是
    //1.根据请求地址获取对应的mapping参数
    //2.调用具体的业务方法
    //3.处理结果返回
}

DispatchHandler中负责请求的处理器

  • HandleMapping:请求查询到处理的方法

  • HandleAdapter:真正负责请求处理

  • HandleResultHandle:响应结果处理

SpringWebFlux实现函数式编程的两个接口:RouterFunction(路由处理接口)和HandleFunction(方法处理接口)

ResourceWebHandle:静态资源处理器

注解式编程模型

  1. 创建Springboot项目

  2. 添加依赖

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.1.14.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.example</groupId>
        <artifactId>Demo8</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>Demo8</name>
        <description>Demo8</description>
        <properties>
            <java.version>1.8</java.version>
        </properties>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-webflux</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
            <!--千万不要在这里限制版本,要统一版本,否则会报错NoSuchEror-->
            <dependency>
                <groupId>io.projectreactor</groupId>
                <artifactId>reactor-core</artifactId>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <!--spring-boot-maven-plugin报红原因是Springboot-parent版本号没有和下面的版本号对应-->
                    <version>2.1.14.RELEASE</version>
                </plugin>
            </plugins>
        </build>
    
    </project>
    
  3. 在配置文件中添加端口号
    在这里插入图片描述

  4. 定义User实体类

    在这里插入图片描述

  5. 编写user的业务层

    public interface UserService {
    
        /**
         * 根据id查询用户
         * @param id 身份证件
         * @return {@link Mono }<{@link User }>
         */
        Mono<User> getUser(int id);
    
        /**
         * 查询所有用户
         * @return {@link Flux }<{@link User }>
         */
        Flux<User> getAllUsers();
    
        /**
         * 添加用户
         * @author carl
         * @param user 用户信息
         * @return Mono<Void>
         * @updateRemark: [本次修改内容]
         */
        Mono<Void> saveUserInfo(Mono<User> user);
    }
    
  6. 实现UserSerivce接口

    @Service
    public class UserServiceImpl implements UserService {
        //创建Map集合存储数据
        private final Map<Integer,User> users = new HashMap<>();
    
        public UserServiceImpl() {
            this.users.put(1,new User("lucy","男",20));
            this.users.put(2,new User("mary","男",30));
            this.users.put(3,new User("jack","女",60));
        }
    
        @Override
        public Mono<User> getUser(int id) {
            return Mono.justOrEmpty(this.users.get(id));
        }
    
        @Override
        public Flux<User> getAllUsers() {
            return Flux.fromIterable(this.users.values());
        }
    
        @Override
        public Mono<Void> saveUserInfo(Mono<User> userMono) {
            return userMono.doOnNext(person->{
                //向map中存入值
                int id = users.size() + 1;
                users.put(id,person);
            }).thenEmpty(Mono.empty());
        }
    }
    
  7. 实现Controller

    @RestController
    public class UserController {
        @Autowired
        private UserService userService;
    
        /**
         * id查询
         * @author carl
         * @param id user的主键
         * @return 返回结果
         * @updateRemark: [本次修改内容]
         */
        @GetMapping("/{id}")
        public Mono<User> getUserById(@PathVariable int id) {
            return userService.getUser(id);
        }
    
        /**
         * 查询所有用户信息
         * @return {@link Flux }<{@link User }>
         */
        @GetMapping("/user")
        public Flux<User> getUsers(){
            return userService.getAllUsers();
        }
    
        /**
         * 保存用户
         * @param user 用户信息
         * @return {@link Mono }<{@link Void }>
         */
        @PostMapping("/save")
        public Mono<Void> saveUser(@RequestBody User user) {
            Mono<User> userMono=Mono.just(user);
            return userService.saveUserInfo(userMono);
        }
    
    }
    

    此时启动SpringBoot,访问http://localhost:8081/1
    在这里插入图片描述
    http://localhost:8081/user

    在这里插入图片描述

函数式编程模型

使用函数式编程模型,和注解式编程模型的区别在于,函数式编程模型操作时需要自己初始化服务器

基于函数式编程模型有两个核心接口:

  1. RouterFunction:实现路由功能,请求转发给对应的Handler

  2. HandleFunction:处理请求生成 响应的函数

核心任务时定义两个函数式接口的实现,并启动需要的服务器

SpringWenFlux请求和响应不再是ServletRequest和ServletResponse,而是ServerRequest和ServerResponse

注解式编程步骤中的前六步都不需要改变

在上面六步的基础上实现以下步骤

  1. 实现Handler

    package com.carl.demo8.handler;
    
    import com.carl.demo8.entity.User;
    import com.carl.demo8.service.UserService;
    import org.springframework.http.MediaType;
    import org.springframework.web.reactive.function.server.ServerRequest;
    import org.springframework.web.reactive.function.server.ServerResponse;
    import reactor.core.publisher.Flux;
    import reactor.core.publisher.Mono;
    
    import static org.springframework.web.reactive.function.BodyInserters.fromObject;
    
    /**
     * @PackageName com.carl.demo8
     * @author carl
     * @Description: TODO 类描述
     * @Version V1.0
     * @Date 2022-11-30 1:57
     * Modified By:TODO
     **/
    public class UserHandler {
        private final UserService userService;
    
        public UserHandler(UserService userService) {
            this.userService = userService;
        }
    
        /**
         * 按id获取用户
         * @param request 请求参数
         * @return {@link Mono }<{@link ServerResponse }>
         */
        public Mono<ServerResponse> getUserById(ServerRequest request){
            int id = Integer.parseInt(request.pathVariable("id"));
            //非空判断
            Mono<ServerResponse> response = ServerResponse.notFound().build();
            Mono<User> user = this.userService.getUser(id);
            //使用Reactor操作符flatMap,将对象以流的形式返回,如果对象为空,则返回一个空对象流
            return user.flatMap(person->ServerResponse.ok()
                    .contentType(MediaType.APPLICATION_JSON)
                    .body(fromObject(person))).switchIfEmpty(response);
        }
    
        /**
         * 获取所有用户
         * @param request 请求参数
         * @return {@link Mono }<{@link ServerResponse }>
         */
        public Mono<ServerResponse> getAllUsers(ServerRequest request){
            Flux<User> users= this.userService.getAllUsers();
            return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(users,User.class);
        }
    
        /**
         * 保存用户
         * @param request 请求参数
         * @return {@link Mono }<{@link ServerResponse }>
         */
        public Mono<ServerResponse> saveUser(ServerRequest request){
            Mono<User> userMono = request.bodyToMono(User.class);
            return ServerResponse.ok().build(this.userService.saveUserInfo(userMono));
        }
    }
    
  2. 初始化服务器,编写Router

    package com.carl.demo8.controller;
    
    import com.carl.demo8.handler.UserHandler;
    import com.carl.demo8.service.UserService;
    import com.carl.demo8.service.impl.UserServiceImpl;
    import org.springframework.web.reactive.function.server.RouterFunction;
    import org.springframework.web.reactive.function.server.RouterFunctions;
    import org.springframework.web.reactive.function.server.ServerResponse;
    
    import static org.springframework.http.MediaType.APPLICATION_JSON;
    import static org.springframework.web.reactive.function.server.RequestPredicates.*;
    
    /**
     * @PackageName com.carl.demo8.controller
     * @author carl
     * @Description: TODO 类描述
     * @Version V1.0
     * @Date 2022-11-30 2:13
     * Modified By:TODO
     **/
    public class Server {
        public RouterFunction<ServerResponse> routerFunction(){
            //创建UserHandler
            UserService userService = new UserServiceImpl();
            UserHandler userHandler = new UserHandler(userService);
            //关联UserHandler
            return RouterFunctions.route(GET("/user/{id}").and(accept(APPLICATION_JSON)),userHandler::getUserById)
                    .andRoute(GET("/users").and(accept(APPLICATION_JSON)),userHandler::getAllUsers)
                    .andRoute(POST("/save").and(accept(APPLICATION_JSON)),userHandler::saveUser);
        }
    }
    
  3. 在Server 类中创建服务器完成适配

    public void createReactorServer(){
        //路由和Handler适配
        RouterFunction<ServerResponse> route=routerFunction();
        HttpHandler httpHandler = toHttpHandler(route);
        ReactorHttpHandlerAdapter reactorHttpHandlerAdapter = new ReactorHttpHandlerAdapter(httpHandler);
        //创建服务器
        HttpServer httpServer = HttpServer.create();
        httpServer.handle(reactorHttpHandlerAdapter).bindNow();
    }
    
  4. 编写执行线程

    public static void main(String[] args) throws IOException {
        Server server=new Server();
        server.createReactorServer();
        System.out.println("enter to exit");
        System.in.read();
    }
    
  5. 运行端口号在日志中打印
    在这里插入图片描述
    此时访问http://localhost:60739/user/1
    在这里插入图片描述
    http://localhost:60739/users
    在这里插入图片描述

5.SpringWebFlux的Client调用
package com.carl.demo8.client;

import com.carl.demo8.entity.User;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

/**
 * @PackageName com.carl.demo8.client
 * @author carl
 * @Description: TODO 类描述
 * @Version V1.0
 * @Date 2022-11-30 2:34
 * Modified By:TODO
 **/
public class Client {
    public static void main(String[] args){
        WebClient webClient = WebClient.create("http://127.0.0.1:55788");
        //调用根据id查询user
        int id = 1;
        User user=webClient.get().uri("/user/{id}",id)
                .retrieve().bodyToMono(User.class).block();
        System.out.println(user.getName());
        //查询所有
        Flux<User> userFlux = webClient.get().uri("/users").accept(MediaType.APPLICATION_JSON).retrieve().bodyToFlux(User.class);
        userFlux.map(users->users.getName()).buffer().doOnNext(System.out::println).blockFirst();
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>tech.pdai</groupId>
    <artifactId>001-spring-framework-demo-helloworld-xml</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <spring.version>5.3.9</spring.version>
        <aspectjweaver.version>1.9.6</aspectjweaver.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>${aspectjweaver.version}</version>
        </dependency>
    </dependencies>

</project>
  • 7
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Carl·杰尼龟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值