WebFlux内部使用的是响应式编程(Reactive Programming),以Reactor库为基础, 基于异步和事件驱动,可以让我们在不扩充硬件资源的前提下,提升系统的吞吐量和伸缩性。

WebFlux使用的技术栈:

【SpringWebFlux】WebFlux入门_flux

【SpringWebFlux】WebFlux入门_webflux_02

使用

引入依赖

<dependency>
	<groupId>org.projectlombok</groupId>
	<artifactId>lombok</artifactId>
</dependency>

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

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

<dependency>
	<groupId>dev.miku</groupId>
	<artifactId>r2dbc-mysql</artifactId>
	<version>0.8.2.RELEASE</version>
</dependency>
  • webflux依赖会自动引入projectreactor依赖;
  • r2dbc依赖类似于jdbc。

配置文件

application.properties

# r2dbc:<driver>://<host>:<port>/<database>
spring.r2dbc.url=r2dbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&userSSL=false&serverTimezone=GMT%2B8
spring.r2dbc.username=user
spring.r2dbc.password=user

数据源的配置格式采用固定的r2dbc:<driver>://<host>:<port>/<database>

实体类

实体类中使用JPA的注解指定表名和主键字段。

package com.morris.spring.webflux.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.relational.core.mapping.Table;

@Table("t_user")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {

    @Id
    private Long id;

    private String name;

    private Integer age;

}

DAO层

DAO接口组要继承ReactiveCrudRepository接口,这样就能使用JPA提供的一系列方法。

package com.morris.spring.webflux.dao;

import com.morris.spring.webflux.entity.User;
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
import org.springframework.stereotype.Component;

@Component
public interface UserDao extends ReactiveCrudRepository<User, Long> {
}

Service层

Service层注入DAO,可以使用DAO继承过来的一系列操作数据库表的方法。

package com.morris.spring.webflux.service;

import com.morris.spring.webflux.dao.UserDao;
import com.morris.spring.webflux.entity.User;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;

@Service
public class UserService {

    @Resource
    private UserDao userDao;

    public Flux<User> all() {
        return userDao.findAll();
    }

    public Mono<User> saveUser(User user) {
        return userDao.save(user);
    }

    public Mono<User> getById(Long id) {
        return userDao.findById(id);
    }

    public Mono<Void> deleteById(Long id) {
        return userDao.deleteById(id);
    }

}

Controller层

Controller层与传统的SpringMVC一样可以使用@RequestMapping注解来映射请求,方法的返回值类型一定要是响应式的Mono或Flux。

package com.morris.spring.webflux.controller;

import com.morris.spring.webflux.entity.User;
import com.morris.spring.webflux.service.UserService;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;

@RestController
@RequestMapping("user")
public class UserController {

    @Resource
    private UserService userService;

    @GetMapping("/")
    public Flux<User> list() {
        return userService.all();
    }

    @PostMapping("/")
    public Mono<User> addUser(@RequestBody User user) {
        return userService.saveUser(user);
    }

    @PutMapping("/{id}")
    public Mono<User> modifyUser(@RequestBody User user, @PathVariable("id") Long id) {
        user.setId(id);
        return userService.saveUser(user);
    }

    @GetMapping("/{id}")
    public Mono<User> getById(@PathVariable("id") Long id) {
        return userService.getById(id);
    }

    @DeleteMapping("/{id}")
    public Mono<Void> deleteById(@PathVariable("id") Long id) {
        return userService.deleteById(id);
    }

}

启动类

package com.morris.spring.webflux;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringWebfluxDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringWebfluxDemoApplication.class, args);
    }

}

函数式WebFlux的使用

除了可以使用穿透的@RequestMapping注解映射请求外,还可以使用函数式方法来映射请求,这也是WebFlux所推荐的。

Handler相当于Controller层。

package com.morris.spring.webflux.handler;

import com.morris.spring.webflux.entity.User;
import com.morris.spring.webflux.service.UserService;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;

/**
 * 函数式WebFlux的使用
 */
@Component
public class UserHandler {

    @Resource
    private UserService userService;

    public Mono<ServerResponse> list(ServerRequest serverRequest) {
        return ServerResponse.ok().body(userService.all(), User.class);
    }

    public Mono<ServerResponse> addUser(ServerRequest serverRequest) {
        return serverRequest.bodyToMono(User.class)
                .flatMap(u -> ServerResponse.ok().body(userService.saveUser(u), User.class));
    }

    public Mono<ServerResponse> modifyUser(ServerRequest serverRequest) {
        String id = serverRequest.pathVariable("id");
        return serverRequest.bodyToMono(User.class)
                .flatMap(u -> {
                    u.setId(Long.valueOf(id));
                    return ServerResponse.ok().body(userService.saveUser(u), User.class);
                });
    }

    public Mono<ServerResponse> getById(ServerRequest serverRequest) {
        String id = serverRequest.pathVariable("id");
        return ServerResponse.ok().body(userService.getById(Long.valueOf(id)), User.class);
    }

    public Mono<ServerResponse> deleteById(ServerRequest serverRequest) {
        String id = serverRequest.pathVariable("id");
        return ServerResponse.ok().build(userService.deleteById(Long.valueOf(id)));
    }

}

配置路由,相当于@RequestMapping的功能。

package com.morris.spring.webflux.handler;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
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.web.reactive.function.server.RequestPredicates.*;

@Configuration
public class UserRoute {

    @Bean
    public RouterFunction<ServerResponse> userRouterFunction(UserHandler userHandler) {
        return RouterFunctions
                .route(GET("/user2/").and(accept(MediaType.APPLICATION_JSON)), userHandler::list)
                .andRoute(POST("/user2/").and(accept(MediaType.APPLICATION_JSON)), userHandler::addUser)
                .andRoute(PUT("/user2/{id}").and(accept(MediaType.APPLICATION_JSON)), userHandler::modifyUser)
                .andRoute(GET("/user2/{id}").and(accept(MediaType.APPLICATION_JSON)), userHandler::getById)
                .andRoute(DELETE("/user2/{id}").and(accept(MediaType.APPLICATION_JSON)), userHandler::deleteById);
    }

}

WebClient的使用

WebClient类似于HttpClient,只不过WebClient是非阻塞的,而HttpClient是阻塞的。

package com.morris.spring.webflux.handler;

import com.morris.spring.webflux.entity.User;
import com.morris.spring.webflux.service.UserService;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;

/**
 * WebClient的使用
 */
@Component
public class WebClientHandler {

    public Mono<ServerResponse> webClient(ServerRequest serverRequest) {

        return serverRequest.bodyToMono(User.class).flatMap(u ->
                WebClient.create("http://localhost:8080/user/")
                .post()
                .body(BodyInserters.fromValue(u))
                .exchangeToMono(t -> t.bodyToMono(User.class))
                .flatMap(t -> ServerResponse.ok().body(BodyInserters.fromValue(t))));
    }

}