在本文章中通过一个案例来演示一下CompletableFuture的使用。
以一个商品查询为例,在微服务架构中,一个商品信息查询会涉及商品基本信息查询、商品评论查询、商品库存查询,每个查询都涉及不同的微服务的调用,如果使用异步编程的方式,应该如何实现呢?
当前演示的案例项目是采用Spring Boot构建的,其中涉及如下类。
- Goods,商品实体对象。
- 独立微服务:
GoodsService,商品基本信息服务
RepoService,商品库存信息服务
CommentService,商品评论信息服务。
GoodsController,Web 请求。
注意:为了尽量减少和本书中无关内容的引入,下面的代码简化了远程通信模块,直接采用
本地调用方式来模拟远程通信。
商品实体对象
商品实例对象类Goods定义如下。
@Getter
@Data
public class Goods {private Integer id;//商品名称private String name;//价格private BigDecimal price;//库存private Integer repo;//购买人数private Integer buyerNum;//评价private List<String> comment;public Goods(Integer id, string name, BigDecimal price){this.id = id; this.name = name; this.price = price;}
}
模拟微服务请求实现类
商品评价信息服务类CommentService定义如下。
@Service
public class CommentService {/*** 返回指定商品的评论* @return*/public List<String> getCommentsByGoodsId(Integer goodsId) {return Arrays.asList("好","一般","很好");}
}
商品基本信息查询服务类GoodsService定义如下。
@Service
public class GoodsService {/*** 查询商品信息* @return*/public List<Goods> queryGoods(){return Arrays.asList(new Goods(1,"电脑",newBigDecimal(5000)), new Goods(2,"手机",newBigDecimal(3000)), new Goods(3,"书",new BigDecimal(99)), new Goods(4,"杯子",newBigDecimal(18)));}
}
商品库存查询服务类RepoService定义如下。
@Service
public class RepoService {/*** 查询指定商品库存* @param goodsId* @return*/public Integer getRepoByGoodsId(Integer goodsId){return new Random( ) .nextInt(1000);}
}
Web请求
GoodsController类提供HTTP接口服务,用来访问商品详细信息,代码如下。
@RestController
public class GoodsController {@AutowiredGoodsService goodsService;@AutowiredCommentService commentService;@AutowiredRepoService repoService;@GetMapping("/goods")public List<Goods> goods() throws ExecutionException, InterruptedException{CompletableFuture<List<Goods>>goodsFuture = CompletableFuture.supplyAsync(()->goodsService.queryGoods());CompletableFuture cf=goodsFuture.thenApplyAsync(goods->{goods.stream( ).map(good1->CompletableFuture.supplyAsync(()->{good1.setRepo(repoService.getRepoByGoodsId(good1.getId())); return good1;}).thenCompose(good2->CompletableFuture.supplyAsync(()->{good2.setComment(commentService.getCommentsByGoodsId(good2.getId())); return good2;}))).toArray(size->new CompletableFuture[size]); return goods;});return (List<Goods>) cf.handleAsync((goods,th)->th!=nul1?"系统繁忙":goods).get( );}
}
其功能说明如下。
- 使用supplyAsync()方法来构建一个CompletableFuture任务,这个任务负责查询所有商品的基本信息。
- 接着通过thenApplyAsyne()方法来连接另外一个任务,也就是当第一个查询商品基本信息的 CompletinStage任务执行结束后,通过异步线程执行第二个任务。
- 在第二个任务中,使用了thenCompose()方法来组合两个CompletionStage任务,完成商品评论和商品库存信息的补充。
- 最终采用handleAsync()方法返回执行结果,这里用handleAsyncO方法的目的是避免将前置任务的异常直接抛给前端。
总的来说,了解了CompletionStage中不同类型的方法之后,就可以随心所欲地在实际业务场景中使用了,建议各位兄弟根据自己的实际业务场景来判断是否有必要使用,然后根据需求尝试合理地应用,从而更好地理解CompletableFuture。
兄弟们,先消化一下上面的内容吧,接下来的文章会带领我们了解一下CompletableFuture的实现原理,敬请期待^_^