SpringBoot2.0 WebFlux响应式编程

一、什么是WebFlux?

在这里插入图片描述

  • WebFlux是异步非阻塞的,SpringMVC是同步阻塞的
  • 响应式一般用Netty或者Servlet 3.1的容器(因为支持异步非阻塞),而Servlet技术栈用的是Servlet容器

二、基于Spring MVC注解的方式

1、添加依赖

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-webflux</artifactId>
		</dependency>

2、案例

  • Mono表示的是包含0或者1个元素的异步序列
  • Flux表示的是包含0到N个元素的异步序列
@RestController
@RequestMapping("/api")
public class WebfluxDemoController {
    @GetMapping("/mono")
    public Mono<String> mono() {
        return Mono.just("hello world");
    }

    @GetMapping(value = "/flux", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<String> flux() {
        return Flux.fromStream(IntStream.range(1, 5).mapToObj(i -> {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "hello--" + i;
        }));
    }
}

应用启动后发现应用运行于Netty上

在这里插入图片描述

浏览器访问http://localhost:8080/api/flux,可以看到Flux可以多次返回数据(通过SSE实现)

3、SSE(Server-Sent Events)

严格地说,HTTP协议无法做到服务器主动推送信息。但是,有一种变通方法,就是服务器向客户端声明,接下来要发送的是流信息

也就是说,发送的不是一次性的数据包,而是一个数据流,会连续不断地发送过来。这时,客户端不会关闭连接,会一直等着服务器发过来的新的数据流,视频播放就是这样的例子。本质上,这种通信就是以流信息的方式,完成一次用时很长的下载

SSE就是利用这种机制,使用流信息向浏览器推送信息。它基于HTTP协议,目前除了IE/Edge,其他浏览器都支持

4、整合MongoDB

1)、添加依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
        </dependency>

2)、主启动类添加@EnableReactiveMongoRepositories注解

3)、添加mongodb配置信息

spring.data.mongodb.uri=mongodb://localhost:27017/webflux

4)、实现代码

@Document(collection = "user")
@Data
public class User {
    @Id
    private String id;

    private String name;

    private Integer age;
}
@Repository
public interface UserRepository extends ReactiveMongoRepository<User, String> {

}
@RestController
@RequestMapping("/api/user")
public class UserController {

    private final UserRepository repository;

    public UserController(UserRepository repository) {
        this.repository = repository;
    }

    /**
     * 以数组形式一次性返回数据
     *
     * @return
     */
    @GetMapping
    public Flux<User> getAllUser() {
        return repository.findAll();
    }

    /**
     * 以SSE形式多次返回数据
     *
     * @return
     */
    @GetMapping(value = "stream/all", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<User> streamGetAll() {
        return repository.findAll();
    }

    /**
     * 新增用户信息
     *
     * @param user
     * @return
     */
    @PostMapping
    public Mono<User> createUser(@RequestBody User user) {
        //Spring Data Jpa里面,新增和修改都是save,有id是修改,id为空是新增
        user.setId(null);
        return this.repository.save(user);
    }

    /**
     * 根据ID删除用户信息
     *
     * @param id
     * @return
     */
    @DeleteMapping("/{id}")
    public Mono<ResponseEntity<Void>> deleteUserById(@PathVariable String id) {
        //deleteById没有返回值,不能判断数据是否存在
        //this.repository.deleteById(id);
        return this.repository.findById(id)
                //当需要操作数据,并返回一个Mono 这时候使用flatMap
                //如果不操作数据,只是转换数据 使用map
                .flatMap(user -> this.repository.delete(user)
                        .then(Mono.just(new ResponseEntity<Void>(HttpStatus.OK))))
                .defaultIfEmpty(new ResponseEntity<>(HttpStatus.NOT_FOUND));
    }

    /**
     * 修改用户信息
     *
     * @param user
     * @return
     */
    @PutMapping("/{id}")
    public Mono<ResponseEntity<User>> updateUser(
            @PathVariable String id,
            @RequestBody User user) {
        return this.repository.findById(id)
                .flatMap(u -> {
                    u.setAge(user.getAge());
                    u.setName(user.getName());
                    return this.repository.save(u);
                })
                .map(u -> new ResponseEntity<>(u, HttpStatus.OK))
                .defaultIfEmpty(new ResponseEntity<>(HttpStatus.NOT_FOUND));
    }

    /**
     * 根据ID查找用户信息
     *
     * @param id
     * @return
     */
    @GetMapping("/{id}")
    public Mono<ResponseEntity<User>> queryById(
            @PathVariable String id) {
        return this.repository.findById(id)
                .map(u -> new ResponseEntity<>(u, HttpStatus.OK))
                .defaultIfEmpty(new ResponseEntity<>(HttpStatus.NOT_FOUND));
    }
}

三、基于RouterFunction的模式

@Component
public class UserHandler {
    private final UserRepository repository;

    public UserHandler(UserRepository repository) {
        this.repository = repository;
    }

    /**
     * 获取所有用户信息
     *
     * @param request
     * @return
     */
    public Mono<ServerResponse> getAllUser(ServerRequest request) {
        return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON_UTF8)
                .body(this.repository.findAll(), User.class);
    }

    /**
     * 新增用户信息
     *
     * @param request
     * @return
     */
    public Mono<ServerResponse> createUser(ServerRequest request) {
        Mono<User> user = request.bodyToMono(User.class);
        return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON_UTF8)
                .body(this.repository.saveAll(user), User.class);
    }

    /**
     * 根据ID删除用户信息
     *
     * @param request
     * @return
     */
    public Mono<ServerResponse> deleteUserById(ServerRequest request) {
        String id = request.pathVariable("id");
        return this.repository.findById(id).flatMap(
                user -> this.repository.delete(user).then(ServerResponse.ok().build())
                        .switchIfEmpty(ServerResponse.notFound().build()));
    }
}
@Configuration
public class AllRouters {
    @Bean
    RouterFunction<ServerResponse> userRouter(UserHandler handler) {
        return RouterFunctions.nest(
                //相当于Controller类上面的@RequestMapping("/api/user")
                RequestPredicates.path("/api/user")
                //获取所有用户信息
                , RouterFunctions.route(RequestPredicates.GET(""), handler::getAllUser)
                        //新增用户信息
                        .andRoute(RequestPredicates.POST("").and(RequestPredicates.accept(MediaType.APPLICATION_JSON_UTF8)), handler::createUser)
                        //根据ID删除用户信息
                        .andRoute(RequestPredicates.DELETE("/{id}"), handler::deleteUserById));
    }
}

猜你喜欢

转载自blog.csdn.net/qq_40378034/article/details/105610184