background
Spring-web is the traditional Spring MVC synchronous blocking Web Framework, while Spring-WebFlux is a new responsive Web Framework introduced in Spring Framework 5.0. Please refer to the previous article String-WebFlux
In field
- A get request
Defining the interface is the same as MVC
@operation (summary = "get xx list ", description =" get XXX list interface ", Responses = {@apiResponse (responseCode = "400", responses = "get XXX list interface ", responses = {@apiResponse (responseCode = "400", }) @getMapping ("/list") public Flux<ResponseVo<ProductListVo>> getXxxxList() {return xxxService.getxxxList().map(ResponseVo::ok); }Copy the code
The difference between WebFlux and MVC is that when a parameter is returned, Flux or Mono, my understanding is that the returned data is changed from a specific object to a data stream, which requires the client to subscribe to the consumption. For Flux and Mono
- Flux represents 0 to N elements in the data flow
- Mono means there are 0 to 1 elements in the data stream
For specific concepts, please refer to Mono and Flux in the Java Reactor framework
- example
Suppose we now have a requirement to query a list of all currently available products by category. The data structure corresponds to the database:
Product_info The product_info table contains the basic information of all products id,name,used,type_id Product_detail_info The detailed information of each product is contained in the product_info table. Product_id,desc,images product_type Product type table type,type_nameCopy the code
If you use traditional MVC, the corresponding logical code is to query the product information table first, and then use THE ID to query details or a table query results out. Then the assembled data can be returned directly to the front end
public List<ProductInfoVo> getProductList(){ List<ProductInfoVo> list = Lists.newArrayList(); List<ProductInfo> productList = productInfoRepository.findAllByUsed(1); // Group by product category Map<Integer,List<ProductInfo>> Map = productList.stream().Collect(Collectors.groupingBy(ProductInfo::getTypeId)); Map.keyset ().stream().foreach (key -> {ProductType typeInfo = typerepository.findById (key); List<ProductInfo> list = map.get(key); // Merge product information with product details List<ProductInfoDetailVo> detailList = list.stream().map(info->convertToDetail(info)).Collect(Collectors.toList()); List.add (ProductInfoVo. Builder () type(typeinfo.getTypename ()).datalist (detailList).build()); }); return list; }Copy the code
How do we do that with Webflux, since we know that everything in Webflux is asynchronous including queries to the database
Here / / keep up with the difference is that instead of directly returns a list of asynchronous returns a flow Flux < ProductInfo > products. = productInfoRepository findAllByUsed (1); FlatMap (item->{Mono<ProductType> typeMono = typeRepository.findById(item.key()); Return typemono.zipwith (item.collectList(), (t, F) -> ProductInfoVo. Builder () type(typeinfo.getTypename ()) // DataList (f.tree ().map(detail->convertToDetail(detail)). DataList (f.tree ().map(detail->convertToDetail(detail)) Collect(Collectors.toList()); .build()); }).sort(Comparator.comparing(ProductInfo::getId));Copy the code
The end result is a Flux stream equivalent to an MVC list. Unlike MVC, we can’t get the result from the previous step in the programming, but need to aggregate the transformation in the whole process to get the desired result. ZipWith is a way to merge two streams.
- Example 2
Another common case is when I’m writing business logic and need to use the return value of the previous method to determine the subsequent code execution logic. In MVC, it’s very simple. You just decide what to return
If (result == 1){else{// result == 1}Copy the code
However, in reactive programming, you can’t get the result directly from the previous method. There are ways to use Mono or Flux’s block() method, but this method is blocking, which defeats the whole purpose of reactive programming.
Mono<ProductInfo> products = productInfoRepository.findByProductId(1); Product.flatmap (product -> mono. just(Optional. Of (product))).defaultifEmpty (Optional. Empty()) .flatMap(detailOptional -> { if (! detailOptional.isPresent()) { return Mono.error(new ServerException(ResponseCode.PRODUCT_ID_ERROR)); }else{XXXX normal logic}});Copy the code
All you need to do is wrap each object in Optional and check it out before you use it.
- Example 3
The same is true with caching. Redis also supports responsiveness, which is used in the same way as MVC, but with a change in the return result just like mysql
private final ReactiveStringRedisTemplate redisTemplate;
Mono<String> stringMono = redisTemplate.opsForValue().get(RedisKey.Product_KEY + id);
Mono<String> ticketFromCache = getTicketFromCache();
stringMono.flatMap(ticket -> Mono.just(Optional.of(ticket))).defaultIfEmpty(Optional.empty())
.flatMap(opt -> opt.<Mono<? extends String>>map(Mono::just).orElseGet(() ->
ticketFromCache.flatMap(ticket -> getUserInfo(ticket, account, ids))
.map(JSONObject::toJSONString))
.map(json -> JSONObject.parseObject(json, ProductInfoVo.class)));
Copy the code
The logic of the above code is to obtain the product information from the cache first. If the information is not in the cache, then query it. The query requires a ticket.