1 依赖
要明确的是在spring5之后,spring开始支持响应式开发,并且内部支持两个响应式编程框架
- rxJava
- project-reactor(默认)
在spring-boot2.0以后,使用的spring版本就是spring5,这里就是用springboot构建:
<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>
</dependency>
</dependencies>
2 基于注释构建Rest服务
这种开发方式和springMvc很类似。
这里就以开发开发一个简单的商品的crud操作的rest服务
sevice:
@Component
public class GoodsService {
/**
* 使用Map模拟数据层
*/
private final Map<String, Goods> goodsMap = new ConcurrentHashMap<>();
@PostConstruct
public void initGoodsMap(){
Goods goods = new Goods();
goods.setId("1");
goods.setName("小米手机");
goodsMap.put(goods.getId(), goods);
}
@ApiOperation("查询所有商品")
public Flux<Goods> getGoods() {
return Flux.fromIterable(goodsMap.values());
}
@ApiOperation("根据id批量查询")
public Flux<Goods> getProductsByIds(final Flux<String> ids) {
return ids.flatMap(id-> Mono.justOrEmpty(this.goodsMap.get(id)));
}
@ApiOperation("根据id查询")
public Mono<Goods> getProductsById(final String id) {
return Mono.justOrEmpty(goodsMap.get(id));
}
@ApiOperation("保存或者更新商品")
public Mono<Void> creatOrUpdate(final Mono<Goods> goodsMono){
return goodsMono
.doOnNext(goods -> goodsMap.put(goods.getId(),goods))
.thenEmpty(Mono.empty());
}
@ApiOperation("根据id删除商品")
public Mono<Goods> deleteById(final String id){
return Mono.justOrEmpty(this.goodsMap.remove(id));
}
}
- controller
@RestController
@RequestMapping("goods")
public class GoodsController {
private final GoodsService goodsService;
public GoodsController(GoodsService goodsService) {
this.goodsService = goodsService;
}
@GetMapping
public Flux<Goods> getGoods(){
return goodsService.getGoods();
}
@GetMapping("/{id}")
public Mono<Goods> getGoodById(@PathVariable("id") final String id){
return goodsService.getProductsById(id);
}
@PostMapping()
public Mono<Void> creatOrUpdate(@RequestBody final Mono<Goods> goods){
return goodsService.creatOrUpdate(goods);
}
@DeleteMapping("/{id}")
public Mono<Goods> deleteGoodById(@PathVariable("id") final String id){
return goodsService.deleteById(id);
}
}
- 模型
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Goods implements Serializable {
@ApiModelProperty("商品id")
private String id;
@ApiModelProperty("商品编码")
private String code;
@ApiModelProperty("商品名字")
private String name;
@ApiModelProperty("商品价格")
private Float price;
}
- 主启动
@SpringBootApplication
public class GoodsServiceApplication {
public static void main(String[] args) {
SpringApplication.run(GoodsServiceApplication.class, args);
}
}
都是flux或者mono作为返回值,这个就是WebFlux应用的特征
关于这两个对象的介绍可参考:
3 基于函数式编程构建Rest服务
spring-webflux中,函数式编程模型的核心概念是Router Functions,对应的就是@Controller和@RequestMapping等springMVc的注解。
Router Functions提供了一套函数式API,用于创建Router和Handler对象。可以简单的认识Router对应@RequestMapping,
Handler对应@Controller
传入的Http请求交由Handler Function处理,Handler Function本质是一个接收ServerRequest
并返回一个Mono<ServerResponse>
的函数
3.1 函数式构建入门程序
- 使用Handler Function定义处理逻辑,类似于Controller注解
import org.springframework.web.reactive.function.server.HandlerFunction;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;
/**
* @author wyaoyao
* @date 2021/5/6 9:45
* 实现HandlerFunction接口
*/
public class HelloWorldHandlerFunction implements HandlerFunction<ServerResponse> {
@Override
public Mono<ServerResponse> handle(ServerRequest serverRequest) {
return ServerResponse.ok().bodyValue("hello web flux");
}
}
HandlerFunction接口内部只有一个方法需要实现。
- 使用Router定义路由规则,比如这里当访问"/hello-world",交给上面HelloWorldHandlerFunction 处理,类似于之前的RequestMapping注解
@Component
public class HelloWorldHandlerRouter {
@Bean
public HandlerFunction helloHandlerFunction(){
return new HelloWorldHandlerFunction();
}
@Bean
public RouterFunction<ServerResponse> responseRouterFunction(@Autowired HelloWorldHandlerFunction helloWorldHandlerFunction){
return RouterFunctions.route()
.GET("/hello-world",helloWorldHandlerFunction)
.build();
}
}
主启动类就省略了,很简单
启动测试,
3.2 基于函数式构建Rest服务
实现上面商品的crud操作
- 提供一个GoodsHandlerFunction
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 study.wyy.spring.reactor.goods.model.Goods;
import study.wyy.spring.reactor.goods.service.GoodsService;
/**
* @author wyaoyao
* @date 2021/5/6 10:51
*/
@Component
public class GoodsHandlerFunction {
private final GoodsService goodsService;
public GoodsHandlerFunction(GoodsService goodsService) {
this.goodsService = goodsService;
}
public Mono<ServerResponse> getGoods(ServerRequest serverRequest){
return ServerResponse.ok().body(this.goodsService.getGoods(),Goods.class);
}
public Mono<ServerResponse> getGoodById(ServerRequest serverRequest){
String id = serverRequest.pathVariable("id");
return ServerResponse.ok().body(goodsService.getProductsById(id),Goods.class);
}
public Mono<ServerResponse> creatOrUpdate(ServerRequest serverRequest){
return ServerResponse.ok()
.body(goodsService.creatOrUpdate(serverRequest.bodyToMono(Goods.class)),Void.class);
}
public Mono<ServerResponse> deleteGoodById(ServerRequest serverRequest){
String id = serverRequest.pathVariable("id");
return ServerResponse.ok().body(goodsService.deleteById(id),Goods.class);
}
}
- 使用router定义endpoint
import static org.springframework.web.reactive.function.server.RequestPredicates.*;
import static org.springframework.web.reactive.function.server.RouterFunctions.route;
/**
* @author wyaoyao
* @date 2021/5/6 16:43
*/
@Configuration
public class GoodsRouter {
@Bean
public RouterFunction<ServerResponse> goodsHandlerFunctionRouter(@Autowired GoodsHandlerFunction handlerFunction) {
return route(GET("/"), handlerFunction::getGoods)
.andRoute(GET("/{id}"), handlerFunction::getGoodById)
.andRoute(POST("/").and(accept(MediaType.APPLICATION_JSON)),handlerFunction::creatOrUpdate)
.andRoute(DELETE("/{id}"),handlerFunction::deleteGoodById);
}
}
-
启动类省略
-
测试