当前位置: 首页 > 科技观察

巧妙利用SpringBoot应用连带责任模型,让编程事半功倍!

时间:2023-03-18 19:36:32 科技观察

1.什么是责任链模型?责任链模式顾名思义就是在请求者和接收者之间建立一个对象处理链接,避免了请求发送者和接收者之间的耦合!责任链模式是一种非常实用的设计模式。典型应用场景包括:ApacheTomcat的Encoding代码处理、SpringBoot中的拦截器、过滤链netty中的处理链、支付风控机制日志处理层面,尤其是当程序的处理流程很长时,采用责任链设计模式,既做到了优雅,又易于复用和扩展!今天我们就来看看如何在SpringBoot中应用责任链模式吧!2.代码实践在SpringBoot中,实践责任链模式有几种方式。今天我们主要介绍三种练习方法。我们以一个订单流程为例,将其分成多个独立的检验逻辑。可能的数据验证流程如下:使用责任链设计模式进行编程,代码实践如下!2.1.方法1首先,我们定义一个简单版本的订单对象OrderContext。publicclassOrderContext{/***请求唯一的序列ID*/privateStringseqId;/***用户ID*/privateStringuserId;/***产品skuId*/privateLongskuId;/***订单数量*/privateIntegeramount;/***用户收货地址ID*/privateStringuserAddressId;//..set,get}然后,我们定义一个数据处理接口OrderHandleIntercept,用于标准化执行!publicinterfaceOrderHandleIntercept{/***指定执行顺序*@return*/intsort();/***处理参数*@paramcontext*@return*/OrderAddContexthandle(OrderAddContextcontext);}接下来,我们分别创建三个不同的接口实现类,并指定执行顺序,如下:RepeatOrderHandleInterceptService:用于逻辑校验重复订单ValidOrderHandleInterceptService:用于验证请求参数是否合法BankOrderHandleInterceptService:用于检查客户账户余额是否充足@ComponentpublicclassRepeatOrderHandleInterceptServiceimplementsOrderHandleIntercept{@Overridepublicintsort(){//重复订单的逻辑验证,顺序执行是1返回1;}@OverridepublicOrderAddContext句柄(或derAddContextcontext){System.out.println("通过seqId查询客户是否重复下单");返回上下文;}}@ComponentpublicclassValidOrderHandleInterceptServiceimplementsOrderHandleIntercept{@Overridepublicintsort(){//用于验证请求参数是否合法,执行顺序为2return2;}@OverridepublicOrderAddContexthandle(OrderAddContextcontext){System.out.println("检查请求参数,是否合法,获取客户银行账号");返回上下文;}}@ComponentpublicclassBankOrderHandleInterceptServiceimplementsOrderHandleIntercept{@Overridepublicintsort(){//用于检查客户账户余额是否充足,执行顺序为3return3;}@OverridepublicOrderAddContexthandle(OrderAddContextcontext){System.out.println("查询银行账户是否合法,调用银行系统查询银行账户余额是否满足订单金额");返回上下文;然后,我们还需要创建一个订单数据验证管理器OrderHandleChainService来管理这些实现类@ComponentpublicclassOrderHandleChainServiceimplementsApplicationContextAware{privateListhandleList=newArrayList<>();@OverridepublicvoidsetApplicationContext(ApplicationContextapplicationContext)throwsBeansException{//获取指定接口实现类,根据sort排序,将Map放入ListserviceMap=applicationContext.getBeansOfType(OrderHandleIntercept.class);handleList=serviceMap.values().stream().sorted(Comparator.comparing(OrderHandleIntercept::sort)).collect(Collectors.toList());}/***执行处理*@paramcontext*@return*/publicOrderAddContextexecute(OrderAddContextcontext){for(OrderHandleIntercepthandleIntercept:handleList){context=handleIntercept.handle(context);}返回上下文;}}最后,我们编写单元测试看看它是如何工作的!@RunWith(SpringRunner.class)@SpringBootTestpublicclassCalculatorServiceTest{@AutowiredprivateOrderHandleChainServiceorderHandleChainService;@Testpublicvoidtest(){orderHandleChainService.execute(newOrderAddContext());}}执行结果如下:通过seqId检查客户是否重复下单,检查请求参数是否合法,获取客户银行账户,检查银行账户是否合法,调用银行系统检查银行账户余额是否符合订单金额。如果需要继续添加验证流程或处理流程,只需要重新实现OrderHandleIntercept接口即可,其他代码无需改动!当然有些同学可能会觉得不习惯这种方式,不喜欢通过sort()来指定顺序,也可以通过下面的方式手动添加排序@ComponentpublicclassOrderHandleChainService{privateListhandleList=newArrayList<>();@AutowiredprivateValidOrderHandleInterceptServicevalidOrderHandleInterceptService;@AutowiredprivateRepeatOrderHandleInterceptServicerepeatOrderHandleInterceptService;@AutowiredprivateBankOrderHandleInterceptServicebankOrderHandleInterceptService;@PostConstructpublicvoidinit(){//依次手动addObjecthandleList.add(repeatOrderHandleInterceptService);handleList.add(validOrderHandleInterceptService);handleList.add(bankOrderHandleInterceptService);}/***执行处理*@paramcontext*@return*/publicOrderAddContextexecute(OrderAddContextcontext){for(OrderHandleIntercepthandleIntercept:handleList){context=handleIntercept.handle(context);}返回上下文;}}2.2、方法二第二种实现方法更简单。我们通过注解@Order来指定排序,而不是手动方法sort(),操作方法如下:/***指定注入顺序为1**/@Order(1)@ComponentpublicclassRepeatOrderHandleInterceptServiceimplementsOrderHandleIntercept{//...省略}/***指定注入顺序为2**/@Order(2)@ComponentpublicclassValidOrderHandleInterceptServiceimplementsOrderHandleIntercept{//...省略}/***指定注入顺序为3**/@Order(3)@ComponentpublicclassBankOrderHandleInterceptService实现OrderHandleIntercept{//...Omit}@ComponentpublicclassOrderHandleChainService{@AutowiredprivateListhandleList;/***执行处理*@paramcontext*@return*/publicOrderAddContextexecute(OrderAddContextcontext){for(OrderHandleIntercepthandleIntercept:handleList){context=handleIntercept.handle(context);}返回上下文;}}运行单元测试,会发现和上面运行的结果是一致的,因为Spring的ioc容器支持通过Map或者List直接注入对象,省去了自己排序2.3.方法三通过定义抽象类实现责任链设计模式。还是以上面的案例为例,我们需要先定义一个抽象类,比如AbstractOrderHandle。publicabstractclassAbstractOrderHandle{/***责任链,下一个链接节点*/privateAbstractOrderHandlenext;/***执行入口*@paramcontext*@return*/publicOrderAddContextexecute(OrderAddContextcontext){context=handle(context);//判断是否有下一个责任链节点,如果没有则说明是最后一个节点if(getNext()!=null){getNext().execute(context);}返回上下文;}/***处理参数*@paramcontext*@return*/publicabstractOrderAddContexthandle(OrderAddContextcontext);publicAbstractOrderHandlegetNext(){返回下一个;}publicvoidsetNext(AbstractOrderHandlenext){this.next=next;}}然后,分别创建三个处理类,并排列序号。@Order(1)@ComponentpublicclassRepeatOrderHandleextendsAbstractOrderHandle{@OverridepublicOrderAddContexthandle(OrderAddContextcontext){System.out.println("通过seqId查询客户是否重复下单");返回上下文;}}@Order(2)@ComponentpublicclassValidOrderHandleextendsAbstractOrderHandle{@OverridepublicOrderAddContexthandle(OrderAddContextcontext){System.out.println("检查请求参数是否有效,获取客户银行账户");返回上下文;}}@Order(3)@ComponentpublicclassBankOrderHandleextendsAbstractOrderHandle{@OverridepublicOrderAddContexthandle(OrderAddContextcontext){System.out.println("查询银行账户是否合法,调用银行系统查询银行账户是否有余额满足订单金额");返回上下文;}}接下来,创建责任链管理器,例如OrderHandleManager。@ComponentpublicclassOrderHandleManager{@AutowiredprivateListorderHandleList;@PostConstructpublicvoidinit(){//如果List没有按照@Order注解排序,可以通过下面的方法手动排序Collections.sort(orderHandleList,AnnotationAwareOrderComparator.INSTANCE);intsize=orderHandleList.size();for(inti=0;i