在开发后台接口时,为了开发效率,我们往往习惯编写串行执行的代码,去调用不同的接口,即使这些接口之间并无依赖,这使得最后开发的接口性能低下,且数据不方便复用
此框架目的旨在保持开发效率的同时,很方便地支持并发和数据复用
当然,在极端高并发的场景下,并行调用接口对性能提升并不明显,但不代表这个项目没有价值.因为互联网世界的大部分应用,并不会有非常高的并发访问量
原理CountDownLatch+Future+递归为了得到目标数据,会递归分析并获取数据做需要的依赖项, 数据的依赖项有两种:其他接口返回值或者输入参数,前一种需要调用其他接口,这个调用将封装为任务异步执行并获取结果特性异步获取依赖
所有 @DataConsumer 定义的依赖将异步获取.当provider方法参数中的所有依赖获取完成,才执行provider方法
不限级嵌套
依赖关系支持深层嵌套.下面的示例只有一层
异常处理
目前支持两种处理方式:忽略or终止
忽略是指provider方法在执行时,忽略抛出的异常并returnnull值;终止是指一旦有一个provider方法抛出了异常,将逐级向上抛出,终止后续处理.
配置支持consumer级或者全局,优先级:consumer级>全局
查询缓存
在调用Facade的query方法的一次查询生命周期内, 方法调用结果可能复用,只要方法签名以及传参一致,则默认方法是幂等的,将直接使用缓存的查询结果. 但这个不是绝对的,考虑到多线程的特性,可能有时候不会使用缓存
超时控制
@DataProvider 注解支持配置timeout,超时将抛出中断异常(InterruptedException),遵循异常处理逻辑
使用方法pom.xml
<dependency><groupId>io.github.lvyahui8</groupId><artifactId>spring-boot-data-aggregator-starter</artifactId><version>1.0.2</version></dependency>application.properties
#指定要扫描注解的包io.github.lvyahui8.spring.base-packages=io.github.lvyahui8.spring.example@DataProvider 定义数据提供者@DataConsumer 定义方法参数依赖类型为其他接口返回值,其他接口是一个@DataProvider@InvokeParameter 定义方法参数依赖类型为用户输入值SpringBean DataBeanAggregateQueryFacade 查询指定的数据的门面示例开发一个用户汇总数据接口,包括用户的基础信息和博客列表
1.定义提供基础数据的"原子"服务使用@DataProvider定义接口为数据提供者
使用@InvokeParameter指定要传递的用户输入参数
博客列表服务
需要参数userId
@ServicepublicclassPostServiceImplimplementsPostService{@DataProvider("posts")@OverridepublicList<Post>getPosts(@InvokeParameter("userId")LonguserId){try{Thread.sleep(1000L);}catch(InterruptedExceptione){//}Postpost=newPost();post.setTitle("springdataaggregateexample");post.setContent("Noactiveprofileset,fallingbacktodefaultprofiles");returnCollections.singletonList(post);}}用户基础信息查询服务
需要参数userId
@ServicepublicclassUserServiceImplimplementsUserService{@DataProvider("user")@OverridepublicUserget(@InvokeParameter("userId")Longid){/**/try{Thread.sleep(100L);}catch(InterruptedExceptione){//}/*mockauser*/Useruser=newUser();user.setId(id);user.setEmail("lvyahui8@gmail.com");user.setUsername("lvyahui8");returnuser;}}2.定义并实现聚合层组合@DataProvider \ @DataConsumer \ @InvokeParameter 实现汇聚功能
@ComponentpublicclassUserAggregate{@DataProvider("userWithPosts")publicUseruserWithPosts(@DataConsumer("user")Useruser,@DataConsumer("posts")List<Post>posts){user.setPosts(posts);returnuser;}}3.调用聚合层接口注解了@DataProvider方法的接口不需要直接调用,而是通过门面类DataBeanAggregateQueryFacade访问.
指定要查询的dataid,查询参数,返回值类型,并调用facade.get方法即可
DataBeanAggregateQueryFacadequeryFacade=context.getBean(DataBeanAggregateQueryFacade.class);Useruser=queryFacade.get(/*dataid*/"userWithPosts",/*InvokeParameters*/Collections.singletonMap("userId",1L),User.class);Assert.notNull(user,"usernotnull");Assert.notNull(user.getPosts(),"userpostsnotnull");运行结果
可以看到,user和posts是由异步线程执行查询,而userWithPosts是主调线程执行,其中
基础user信息查询耗费时间1000ms用户博客列表查询耗费时间1000ms总的查询时间1005ms[aggregateTask-1]queryid:user,costTime:1000ms,resultType:User,invokeMethod:UserServiceImpl#get[aggregateTask-2]queryid:posts,costTime:1000ms,resultType:List,invokeMethod:PostServiceImpl#getPosts[main]queryid:userWithPosts,costTime:1010ms,resultType:User,invokeMethod:UserAggregate#userWithPosts[main]user.name:lvyahui8,user.posts.size:1
评论