WebFlux内部使用的是响应式编程(Reactive Programming),以Reactor库为基础, 基于异步和事件驱动,可以让我们在不扩充硬件资源的前提下,提升系统的吞吐量和伸缩性。
WebFlux使用的技术栈:
使用
引入依赖
<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))));
}
}