原文链接:https://dzone.com/articles/spring-webflux-first-steps
译者:jackzhaiyh
Spring 5 新增WebFlux——在Spring应用中支持响应式编程实践。下面看一下如何将其引入旧版注解风格Controller中。
Spring WebFlux用于表示Spring web层中对Reactive programming的支持。它为服务端创建响应式web应用、也为客户端消费REST服务提供支持。
本文中,我会演示一个采用Spring WebFlux的示例web应用。Spring 5+中的WebFlux支持两种不同编程风格:传统注解风格和新的函数式风格。本文中,我将坚持传统的注解风格,并尝试在另一篇博客中详细描述类似的应用程序,但是以函数式风格定义endpoints。我的重点将是编程模型。
如下是一个相当简单的REST接口,它支持一个酒店资源的CRUD操作:
public class Hotel { private UUID id; private String name; private String address; private String state; private String zip; ....}
示例中采用Cassandra存储这个entity,并且使用Spring Data Cassandra对响应式编程的支持使数据层获得响应式特性,从而可以支持这样的API。我采用两个repositories,一个便于上面酒店entity的存储,另一个维护可以通过酒店第一个字母进行检索的冗余数据。
public interface HotelRepository { Mono save(Hotel hotel); Mono update(Hotel hotel); Mono findOne(UUID hotelId); Mono delete(UUID hotelId); Flux findByState(String state);}public interface HotelByLetterRepository { Flux findByFirstLetter(String letter); Mono save(HotelByLetter hotelByLetter); Mono delete(HotelByLetterKey hotelByLetterKey);}
返回一个entity实例的操作现在返回一个Mono类型,返回多个entity实例的则返回一个Flux类型。
鉴于此,我简单介绍一种返回响应类型的方法:当更新一个酒店实体时,我必须先删除HotelByLetter存储库维护的冗余数据,然后重新创建一个。这可以通过使用Flux和Mono类型提供的优秀运算符来完成,如下所示:
public Mono update(Hotel hotel) { return this.hotelRepository.findOne(hotel.getId()) .flatMap(existingHotel -> this.hotelByLetterRepository.delete(new HotelByLetter(existingHotel).getHotelByLetterKey()) .then(this.hotelByLetterRepository.save(new HotelByLetter(hotel))) .then(this.hotelRepository.update(hotel))).next();}
现在回到文章的焦点:支持Web层中基于注解的响应式编程模型支持!
多年来,@Controller和@RestController注解一直是Spring MVC的REST endpoint支持的重要组成部分。传统上,他们已经使用多时并返回java POJO。响应模型中的这些控制器已经被调整用来接收和返回Reactive类型——在我的例子中是Mono和Flux——但是另外也支持Rx-java和Reactive Streams类型。
鉴于此,我的Controller如下所示:
@RestController@RequestMapping('/hotels')public class HotelController { .... @GetMapping(path = '/{id}') public Mono get(@PathVariable('id') UUID uuid) { return this.hotelService.findOne(uuid); } @PostMapping public Mono<>> save(@RequestBody Hotel hotel) { return this.hotelService.save(hotel) .map(savedHotel -> new ResponseEntity<>(savedHotel, HttpStatus.CREATED)); } @PutMapping public Mono<>> update(@RequestBody Hotel hotel) { return this.hotelService.update(hotel) .map(savedHotel -> new ResponseEntity<>(savedHotel, HttpStatus.CREATED)) .defaultIfEmpty(new ResponseEntity<>(HttpStatus.NOT_FOUND)); } @DeleteMapping(path = '/{id}') public Mono<>> delete( @PathVariable('id') UUID uuid) { return this.hotelService.delete(uuid).map((Boolean status) -> new ResponseEntity<>('Deleted', HttpStatus.ACCEPTED)); } @GetMapping(path = '/startingwith/{letter}') public Flux findHotelsWithLetter( @PathVariable('letter') String letter) { return this.hotelService.findHotelsStartingWith(letter); } @GetMapping(path = '/fromstate/{state}') public Flux findHotelsInState( @PathVariable('state') String state) { return this.hotelService.findHotelsInState(state); }}
传统的@RequestMapping、@GetMapping和@PostMapping并没有被改变,变得是返回类型。比如,预期只有一个结果的,现在返回一个Mono类型,之前预期返回一个列表的,现在则返回一个Flux类型。
通过使用Spring Data Cassandra中的响应式支持,整个Web到服务到后端设置都是响应式的,特别针对本文的关注点,非常直观易见。
简单地尝试这篇文章后面的代码可能会更易于理解,你可以在我的GitHub仓库中获得这些代码。
联系客服