目录
前言
模版模式应该是工作中最常用的设计模式之一,直白的讲就是如果的一些处理方式是有一定的模版流程处理的,那么在应用中使用该模式在合适不过了。对于其基本的业务应用,我简单写了以下三个基本的通用模版(业务失败重试机制、业务前置检查流程模版、Thrift远程调用处理模版)来展示,有问题的可以留言纠正,谢谢!相关源码见:ZYF基本练习
基本理解与应用举例
GoF《设计模式》一书中,定义如下:
Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm’s structure.
基本结合业务使用可以翻译为:模板方法模式在一个方法中定义一个算法骨架,并将某些步骤推迟到子类中实现。模板方法模式可以让子类在不改变算法整体结构的情况下,重新定义算法中的某些步骤。算法骨架,其实就是指的我们定义的业务处理流程模版。
应用举例平时在看源码的时候基本都可以看到,比方说以下的几个举例:
- spring 中 Hibernate 比如开启事务、获取 Session、关闭 Session 等规范化流程的设计和实现;
- HttpServlet的service()模板方法,定义了整个HTTP请求的执行流程,doGet()、doPost()是模板中可以由子类来定制的部分;
- Java IO类库中,比如InputStream、OutputStream、Reader、Writer的设计都使用了模板模式
业务应用
实际中只有业务上有很固定的流程基本都可以使用模版模式进行定义和解决问题,这边的就我自身来看,具体的业务的一定不方便拿出来举例,所以暂时想了以下三个业务场景较为公用的拿出来与大家分享,有需要纠正的请大家留言纠正。
业务失败重试机制
我们在实际业务处理中经常会遇到调用远程方法如果因为超时或一些其他原因失败的话,最好在进行重试调用一次,而重试简单的来看主要的模版流程基本有按业务实际调用失败后规定休眠一段时间后再次重试一次,如果超过重试次数则返回报错,如果中间重试成功则按成功的处理,所以我们可以简单的写一个业务失败重试机制的模版
基本模版代码
package org.zyf.javabasic.designpatterns.strategy.template.retry; import lombok.extern.log4j.Log4j2; / * @author yanfengzhang * @description 模板方式处理重试逻辑 * @date 2022/3/15 00:13 */ @Log4j2 public abstract class RetryTemplate
{ / * 默认重试次数 */ private static final int DEFAULT_RETRY_TIME = 1; / * 定义重试次数 */ private int retryTime = DEFAULT_RETRY_TIME; / * 重试的睡眠时间 */ private int sleepMills = 0; / * 业务处理的睡眠时间 * * @return 睡眠时间 */ public int getSleepMills() { return sleepMills; } / * 获取重试次数 * * @return 重试次数 */ public int getRetryTime() { return retryTime; } / * 规定业务处理的睡眠时间 * * @param sleepMills 睡眠时间(毫秒) * @return 重试模版 */ public RetryTemplate
setSleepMills(int sleepMills) { if (sleepMills < 0) { throw new IllegalArgumentException("sleepMills should equal or bigger than 0"); } this.sleepMills = sleepMills; return this; } / * 规定业务处理的重试次数 * * @param retryTime 业务处理的重试次数 * @return 重试模版 */ public RetryTemplate
setRetryTime(int retryTime) { if (retryTime <= 0) { throw new IllegalArgumentException("retryTime should bigger than 0"); } this.retryTime = retryTime; return this; } / * 重试的业务执行代码 * 失败时请抛出一个异常 */ protected abstract Object doBiz() throws Exception; / * 重试的主要业务逻辑 * * @return 实际业务处理返回内容 * @throws InterruptedException 中断异常 */ public R execute() throws InterruptedException { for (int i = 0; i < retryTime; i++) { try { return (R) doBiz(); } catch (Exception e) { log.warn("业务执行出现异常,e: ", e); Thread.sleep(sleepMills); } } return null; } }
具体业务处理
package org.zyf.javabasic.designpatterns.strategy.template.retry; import com.google.common.collect.Maps; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; import java.util.Map; import java.util.Random; / * @author yanfengzhang * @description * @date 2022/3/15 00:20 */ @Service @Slf4j public class RetryBizService { private static Map
aphorisms = Maps.newHashMap(); static { aphorisms.put(1, "知人者智,自知者明。胜人者有力,自胜者强。——老子"); aphorisms.put(2, "要知道对好事的称颂过于夸大,也会招来人们的反感轻蔑和嫉妒。——培根"); aphorisms.put(5, "业精于勤,荒于嬉;行成于思,毁于随。——韩愈"); aphorisms.put(7, "最大的骄傲于最大的自卑都表示心灵的最软弱无力。——斯宾诺莎"); aphorisms.put(9, "知之者不如好之者,好之者不如乐之者。——孔子"); } / * 获取名言警句 * * @return 名言警句 */ public String getAphorisms() throws Exception { int randomNumber = new Random().nextInt(10); String rersult = aphorisms.get(randomNumber); if (StringUtils.isBlank(rersult)) { throw new Exception("抱歉!系统异常,暂无数据可进行返回!randomNumber=" + randomNumber); } return rersult; } }
使用及测试展示
测试代码
package org.zyf.javabasic.designpatterns.strategy.template.retry; import com.github.rholder.retry.RetryException; import com.github.rholder.retry.Retryer; import com.github.rholder.retry.RetryerBuilder; import com.github.rholder.retry.StopStrategies; import com.github.rholder.retry.WaitStrategies; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import org.zyf.javabasic.ZYFApplication; import java.util.Objects; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; / * @author yanfengzhang * @description * @date 2022/3/2022/3/15 00:23 */ @RunWith(SpringRunner.class) @SpringBootTest(classes = ZYFApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @Slf4j public class RetryTemplateTest { @Autowired private RetryBizService retryBizService; @Test public void testRetryBizService() { String aphorisms = null; try { aphorisms = (String) new RetryTemplate() { @Override protected Object doBiz() throws Exception { return retryBizService.getAphorisms(); } }.setRetryTime(3).setSleepMills(200).execute(); } catch (Exception e) { log.warn("[RetryBizService名言警句展示] 系统系统进行重试调用失败!"); } if (StringUtils.isEmpty(aphorisms)) { log.error("[RetryBizService名言警句展示异常,请稍后重试]"); return; } log.info("测试成功,得到的名言警句为:{}", aphorisms); } }
测试结果展示

使用框架实现业务失败重试机制
但是实际中,以上的实现还是较为简单,比如像高并发等场景基本没有进行考虑,所以实际应用中我建议使用guava的重试模版进行处理比较好
引入pom
com.github.rholder
guava-retrying
2.0.0
使用及测试展示
package org.zyf.javabasic.designpatterns.strategy.template.retry; import com.github.rholder.retry.RetryException; import com.github.rholder.retry.Retryer; import com.github.rholder.retry.RetryerBuilder; import com.github.rholder.retry.StopStrategies; import com.github.rholder.retry.WaitStrategies; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import org.zyf.javabasic.ZYFApplication; import java.util.Objects; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; / * @author yanfengzhang * @description * @date 2022/3/2022/3/15 00:23 */ @RunWith(SpringRunner.class) @SpringBootTest(classes = ZYFApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @Slf4j public class RetryTemplateTest { @Autowired private RetryBizService retryBizService; @Test public void testGuavaRetrying() { Callable
callable = () -> { /*业务逻辑*/ return retryBizService.getAphorisms(); }; /*定义重试器*/ Retryer
retryer = RetryerBuilder.
newBuilder() /*如果结果为空则重试*/ .retryIfResult(Objects::isNull) /*发生IO异常则重试*/ .retryIfExceptionOfType(Exception.class) /*发生运行时异常则重试*/ .retryIfRuntimeException() /*等待*/ .withWaitStrategy(WaitStrategies.incrementingWait(100, TimeUnit.MILLISECONDS, 100, TimeUnit.MILLISECONDS)) /*允许执行4次(首次执行 + 最多重试3次)*/ .withStopStrategy(StopStrategies.stopAfterAttempt(4)) .build(); try { /*执行*/ String aphorisms = retryer.call(callable); if (StringUtils.isEmpty(aphorisms)) { log.error("[RetryBizService名言警句展示异常,请稍后重试]"); return; } log.info("测试成功,得到的名言警句为:{}", aphorisms); } catch (RetryException | ExecutionException e) { /*重试次数超过阈值或被强制中断*/ log.warn("[RetryBizService名言警句展示] 系统系统进行重试调用失败!"); } } }
业务前置检查流程模版
平时我们对于业务的基本入参可能会有一些指定的业务检查流程需要检查,比方依次有基础参数检查、业务逻辑检查、冲突关联检查、验证码校验、二次弹窗校验等,于是将其整合一下即可有业务前置检查流程模版。
基本代码依赖
package org.zyf.javabasic.designpatterns.strategy.template.check; import lombok.Builder; import lombok.Data; / * @author yanfengzhang * @description 校验基本返回结构 * @date 2022/3/16 23:29 */ @Data @Builder public class CheckResponse { / * 成功标志:true-校验通过,false-校验未通过 */ private boolean pass; / * 校验码 */ private Integer code; / * 未通过原因 */ private String errorMsg; }
模版代码准备
package org.zyf.javabasic.designpatterns.strategy.template.check; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; / * @author yanfengzhang * @description 业务数据校验模版 * @date 2022/3/16 23:35 */ @Slf4j @Service public abstract class BizCheckTemplate { / * 校验全流程模版 * * @param param 基本入参 * @return 校验结果 * @throws Exception 业务异常 */ public CheckResponse checkProcess(P param) { /*1.基础参数校验*/ CheckResponse checkResponse = checkParam(param); if (!checkResponse.isPass()) { return checkResponse; } /*2.业务逻辑校验*/ checkResponse = checkBiz(param); if (!checkResponse.isPass()) { return checkResponse; } /*3.冲突关联校验*/ checkResponse = checkConflict(param); if (!checkResponse.isPass()) { return checkResponse; } /*4.验证码校验*/ checkResponse = checkVerifyCode(param); if (!checkResponse.isPass()) { return checkResponse; } /*5.二次弹窗校验*/ checkResponse = checkTwicePopup(param); if (!checkResponse.isPass()) { return checkResponse; } return checkResponse; } protected abstract CheckResponse checkParam(P param); protected abstract CheckResponse checkBiz(P param); protected abstract CheckResponse checkTwicePopup(P param); protected abstract CheckResponse checkVerifyCode(P param); protected abstract CheckResponse checkConflict(P param); }
业务应用前置检查
假如有一个申诉的业务系统,其需要进行以上前置的检查处理,我们定义其基本检查的实现类如下:
申诉提交检查
package org.zyf.javabasic.designpatterns.strategy.template.check; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; / * @author yanfengzhang * @description 申诉内容创建检查 * @date 2022/3/16 20:24 */ @Service @Slf4j public class AppealCreateCheck extends BizCheckTemplate
{ @Autowired private AppealCheckService appealCheckService; / * 申诉内容参数检查 * * @param param 请求参数信息 * @return 校验结果 */ @Override protected CheckResponse checkParam(AppealMatters param) { if (!param.getType().equals(1)) { /*为了用于测试*/ return CheckResponse.builder().pass(false).errorMsg("新增申诉内容类型不符合要求!").build(); } return CheckResponse.builder().pass(true).build(); } / * 申诉内容业务逻辑检查 * * @param param 请求参数信息 * @return 校验结果 */ @Override protected CheckResponse checkBiz(AppealMatters param) { /*业务检查1:申诉的内容编码是唯一的,当前重复申诉*/ /*业务检查2:申诉的内容项不得包含非法指定的相关内容*/ /*业务检查3:申诉人对应的申诉项中和申诉人当前享有的权益不匹配*/ return CheckResponse.builder().pass(true).build(); } / * 申诉二次弹窗检查(不涉及直接返回通过) * * @param param 请求参数信息 * @return 校验结果 */ @Override protected CheckResponse checkTwicePopup(AppealMatters param) { return CheckResponse.builder().pass(true).build(); } / * 申诉验证码校验(不涉及直接返回通过) * * @param param 请求参数信息 * @return 校验结果 */ @Override protected CheckResponse checkVerifyCode(AppealMatters param) { return CheckResponse.builder().pass(true).build(); } / * 申诉关联冲突校验 * * @param param 请求参数信息 * @return 校验结果 */ @Override protected CheckResponse checkConflict(AppealMatters param) { return appealCheckService.checkConflict(param); } }
申诉修改检查
package org.zyf.javabasic.designpatterns.strategy.template.check; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; / * @author yanfengzhang * @description 申诉内容修改检查 * @date 2022/3/16 23:43 */ @Service @Slf4j public class AppealUpdateCheck extends BizCheckTemplate
{ @Autowired private AppealCheckService appealCheckService; / * 申诉内容参数检查 * * @param param 请求参数信息 * @return 校验结果 */ @Override protected CheckResponse checkParam(AppealMatters param) { if (!param.getType().equals(2)) { /*为了用于测试*/ return CheckResponse.builder().pass(false).errorMsg("修改申诉内容类型不符合要求!").build(); } return CheckResponse.builder().pass(true).build(); } / * 申诉内容业务逻辑检查 * * @param param 请求参数信息 * @return 校验结果 */ @Override protected CheckResponse checkBiz(AppealMatters param) { /*业务检查1:申诉的内容编码不能修改*/ /*业务检查2:申诉的内容项已进行处理的不能在修改,新增的申诉项不得包含非法指定的相关内容*/ /*业务检查3:新增的申诉项中,申诉人对应的申诉项中和申诉人当前享有的权益不匹配*/ return CheckResponse.builder().pass(true).build(); } / * 申诉二次弹窗检查 * * @param param 请求参数信息 * @return 校验结果 */ @Override protected CheckResponse checkTwicePopup(AppealMatters param) { /*二次弹窗检查检查1:申诉的内容存在修改次数,请确认是否修改*/ return CheckResponse.builder().pass(true).build(); } / * 申诉验证码校验 * * @param param 请求参数信息 * @return 校验结果 */ @Override protected CheckResponse checkVerifyCode(AppealMatters param) { /*验证码校验检查1:修改操作需本人进行验证码确认*/ return CheckResponse.builder().pass(true).build(); } / * 申诉关联冲突校验 * * @param param 请求参数信息 * @return 校验结果 */ @Override protected CheckResponse checkConflict(AppealMatters param) { return appealCheckService.checkConflict(param); } }
申诉删除检查
package org.zyf.javabasic.designpatterns.strategy.template.check; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; / * @author yanfengzhang * @description 申诉内容删除检查 * @date 2022/3/16 23:44 */ @Service @Slf4j public class AppealDeleteCheck extends BizCheckTemplate
{ @Autowired private AppealCheckService appealCheckService; / * 申诉内容参数检查 * * @return 校验结果 * @id id 请求参数信息 */ @Override protected CheckResponse checkParam(Long id) { if (!checkPosInteger(id)) { return CheckResponse.builder().pass(false).errorMsg("申诉内容id不合法!").build(); } /*id必须为有效ID*/ return CheckResponse.builder().pass(true).build(); } / * 申诉内容业务逻辑检查 * * @return 校验结果 * @id id 请求参数信息 */ @Override protected CheckResponse checkBiz(Long id) { /*业务检查1:申诉的内容必须存在*/ /*业务检查2:申诉的内容项全部处理完成的不能删除*/ /*业务检查3:申诉人当前享有的权益存在需要进行续费的暂时不能删除申诉内容*/ return CheckResponse.builder().pass(true).build(); } / * 申诉二次弹窗检查 * * @return 校验结果 * @id id 请求参数信息 */ @Override protected CheckResponse checkTwicePopup(Long id) { /*二次弹窗检查检查1:申请确认是否删除*/ /*二次弹窗检查检查2:相关未享受的权益删除后不再享有,申请确认是否删除*/ return CheckResponse.builder().pass(true).build(); } / * 申诉验证码校验 * * @return 校验结果 * @id id 请求参数信息 */ @Override protected CheckResponse checkVerifyCode(Long id) { /*验证码校验检查1:修改操作需本人进行验证码确认*/ return CheckResponse.builder().pass(true).build(); } / * 申诉关联冲突校验(不涉及直接返回通过) * * @return 校验结果 * @id id 请求参数信息 */ @Override protected CheckResponse checkConflict(Long id) { return CheckResponse.builder().pass(true).build(); } / * ID信息合法性:必须为正整数 * * @param id id信息 * @return true-合法;false-非法 */ public static boolean checkPosInteger(Long id) { return null != id && id > 0L; } }
公用检查
package org.zyf.javabasic.designpatterns.strategy.template.check; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; import java.util.Objects; / * @author yanfengzhang * @description 申诉基本校验检查(公用) * @date 2022/3/16 23:40 */ @Service @Slf4j public class AppealCheckService { / * 公用的创建和修改必要参数检查 * * @param param 必要参数信息 * @return 检查结果 */ public CheckResponse checkParam(AppealMatters param) { if (Objects.isNull(param)) { return CheckResponse.builder().pass(false).errorMsg("创建消息不能为空!").build(); } if (StringUtils.isBlank(param.getCode())) { return CheckResponse.builder().pass(false).errorMsg("申诉编码不能为空!").build(); } if (Objects.isNull(param.getType())) { return CheckResponse.builder().pass(false).errorMsg("申诉类型不能为空且必须为规定的类型!").build(); } return CheckResponse.builder().pass(true).build(); } / * 公用的创建和修改关联冲突校验 * * @param param 请求参数信息 * @return 检查结果 */ public CheckResponse checkConflict(AppealMatters param) { /*关联冲突检查1:申诉的内容对应的申诉类型在当前系统指定暂停办理中,请进行替换*/ /*关联冲突检查2:申诉的内容项直接存在相同申诉问题,请重新归类提交*/ /*关联冲突检查3:申诉的内容项与实际享有权益存在冲突,请确认避免后续被系统锁定*/ /*关联冲突检查4:申诉人当前存在其他非法内容,则本次提交需解决之前的内容*/ return CheckResponse.builder().pass(true).build(); } }
业务实现
package org.zyf.javabasic.designpatterns.strategy.template.check; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; / * @author yanfengzhang * @description 申诉相关业务逻辑 * @date 2022/3/16 22:02 */ @Service @Slf4j public class AppealService { @Autowired private AppealCheckService appealCheckService; @Autowired private AppealCreateCheck appealCreateCheck; @Autowired private AppealUpdateCheck appealUpdateCheck; @Autowired private AppealDeleteCheck appealDeleteCheck; / * 申诉内容留档 * * @param appealMatters 申诉内容 */ public void saveAppealMatters(AppealMatters appealMatters) throws Exception { /*1.基本必要参数检查*/ CheckResponse checkResponse = appealCheckService.checkParam(appealMatters); if (!checkResponse.isPass()) { throw new Exception(checkResponse.getErrorMsg()); } /*2.实际业务操作*/ if (checkPosInteger(appealMatters.getId())) { checkResponse = appealCreateCheck.checkProcess(appealMatters); if (!checkResponse.isPass()) { throw new Exception(checkResponse.getErrorMsg()); } /*业务处理,省略*/ } checkResponse = appealUpdateCheck.checkProcess(appealMatters); if (!checkResponse.isPass()) { throw new Exception(checkResponse.getErrorMsg()); } /*业务处理,省略*/ } / * 申诉内容留档 * * @param appealMattersId 申诉内容id */ public void deleteAppealMatters(Long appealMattersId) throws Exception { /*1.基本必要参数检查*/ CheckResponse checkResponse = appealDeleteCheck.checkProcess(appealMattersId); if (!checkResponse.isPass()) { throw new Exception(checkResponse.getErrorMsg()); } /*业务处理,省略*/ } / * ID信息合法性:必须为正整数 * * @param id id信息 * @return true-合法;false-非法 */ public static boolean checkPosInteger(Long id) { return null != id && id > 0L; } }
测试及结果展示
package org.zyf.javabasic.designpatterns.strategy.template.check; import lombok.extern.slf4j.Slf4j; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import org.zyf.javabasic.ZYFApplication; import java.util.Arrays; import java.util.List; / * @author yanfengzhang * @description * @date 2022/3/17 00:15 */ @RunWith(SpringRunner.class) @SpringBootTest(classes = ZYFApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @Slf4j public class BizCheckTemplateTest { @Autowired private AppealService appealService; @Test public void testAppealService() throws Exception { List
appealItems = Arrays.asList("隔壁搬来的邻居真他妈的吵!", "小区的健身房器械就不能在多一些练腿的?"); AppealMatters appealMatters = AppealMatters.builder() .code("shvhf2736gbbnvhbb") .declarant("zyf") .type(2) .appealItems(appealItems).build(); log.info("测试新建"); appealService.saveAppealMatters(appealMatters); log.info("测试更新"); appealMatters.setId(7L); appealMatters.setType(2); appealService.saveAppealMatters(appealMatters); log.info("测试删除"); appealService.deleteAppealMatters(7L); } }
结果展示

Thrift远程调用处理模版
平时可能在使用调用thrift接口的时候往往会使用分页获取调用或者使用滚动的方式进行处理,我们针对这样的业务场景页也共用的定义一套业务模版去处理,具体如下:
基本常规业务处理模版
package org.zyf.javabasic.designpatterns.strategy.template.thrift; import com.google.common.collect.Lists; import lombok.Data; import lombok.extern.slf4j.Slf4j; import java.util.List; import java.util.Objects; / * @author yanfengzhang * @description 业务thrift调用基本类 * * TC : thrift请求 * * TR : thrift返回 * * FR : 业务实际真正要的处理结果 * @date 2022/3/18 23:18 */ @Data @Slf4j public abstract class ThriftInvokeCommand
{ / * thrift请求 */ protected TC command; / * 每个命令最多翻滚多少页 */ private int maxPageCount = ConfigUtils.getThriftScrollMaxPageLimit(); / * 每个命令的最大重试个数 */ private int maxRetryCount = ConfigUtils.getThriftMaxInvokeRetryCount(); / * 每个命令的休眠时间 */ private int sleepMs = ConfigUtils.getThriftScrollRate(); / * 每个命令的查询页数 */ private int pageSize = ConfigUtils.getThriftScrollPageSize(); public ThriftInvokeCommand(TC command) { this.command = command; } / * 核心处理逻辑:获取指定结果 */ public List
getResult() { /*执行的页数*/ int currPageNo = 0; /*command 设置 pageSize*/ setPageSize(command, pageSize); /*调用接口的失败次数*/ int errorCount = 0; TR thriftResult = null; List
rList = Lists.newArrayList(); while (true) { try { /*1.前置处理*/ preHandle(); /*2.业务转发调用*/ thriftResult = invoke(command); /*3.从thriftResult提取结果并转换为实际需要的*/ rList.addAll(convertThriftResult(thriftResult)); /*4.后置处理*/ afterHandle(thriftResult); } catch (Exception e) { log.error("", e); errorCount++; } /*thrift调用无结果则直接停止循转*/ if (Objects.isNull(thriftResult) || 0 == getResultSize(thriftResult)) { break; } if (errorCount >= maxRetryCount || currPageNo >= maxPageCount) { String msg = errorCount >= maxRetryCount ? "重试次数过多,已达最大尝试次数:" + maxRetryCount : "循环次数过多,已达最大尝试次数:" + maxPageCount; log.warn("ThriftInvokeCommand 处理警告:{}", msg); break; } currPageNo++; /*5.停顿,降低调用频率*/ sleepInvoke(); } return rList; } / * thrift调用前置处理 */ protected abstract void preHandle(); / * thrift调用后置处理 * * @param thriftResult thrift调用结果 */ protected abstract void afterHandle(TR thriftResult); / * 核心逻辑, 通过命令获取结果 * * @param command thrift调用请求 * @return thrift调用结果 */ protected abstract TR invoke(TC command) throws Exception; / * command 设置 pageSize */ protected abstract void setPageSize(TC command, int pageSize); / * 从 thriftResult 获取 结果长度,若是 0 ,那么 break 退出 */ protected abstract int getResultSize(TR thriftResult); / * 从 thriftResult 提取结果 */ protected abstract List
convertThriftResult(TR thriftResult); / * 休眠调整处理 */ private void sleepInvoke() { if (sleepMs <= 0) { return; } try { Thread.sleep(sleepMs); } catch (InterruptedException ignored) { } } public ThriftInvokeCommand setPageSize(int pageSize) { this.pageSize = pageSize; return this; } public ThriftInvokeCommand setSleepMs(int sleepMs) { this.sleepMs = sleepMs; return this; } }
分页处理模版
package org.zyf.javabasic.designpatterns.strategy.template.thrift; / * @author yanfengzhang * @description 用于利用分页形式滚动调用thrift * @date 2022/3/18 23:25 */ public abstract class ThriftInvokePageCommand
extends ThriftInvokeCommand
{ private int pageNo = 1; public ThriftInvokePageCommand(TC command) { super(command); } @Override public void preHandle() { /*command 设置 pageNo*/ setPageNo(command, pageNo); } @Override public void afterHandle(TR thriftResult) { /*页数加 1*/ pageNo++; } / * command 设置 pageNo */ protected abstract void setPageNo(TC command, int pageNo); }
滚动处理模版
package org.zyf.javabasic.designpatterns.strategy.template.thrift; / * @author yanfengzhang * @description 用于利用scroll形式滚动调用thrift * @date 2022/3/18 23:23 */ public abstract class ThriftInvokeScrollCommand
extends ThriftInvokeCommand
{ private Long scrollId = 0L; public ThriftInvokeScrollCommand(TC command) { super(command); } @Override public void preHandle() { /*command 设置 ScrollId*/ setScrollId(command, scrollId); } @Override public void afterHandle(TR thriftResult) { /*从 thriftResult 获取 scrollId*/ scrollId = getNextScrollId(thriftResult); } / * command 设置 ScrollId */ protected abstract void setScrollId(TC command, Long scrollId); / * 从 thriftResult 获取 scrollId */ protected abstract Long getNextScrollId(TR thriftResult); }
业务使用模版定义
假设获取电影资源thrift有两套模版基本如下
获取电影thrift接口分页处理
package org.zyf.javabasic.designpatterns.strategy.template.thrift; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections.CollectionUtils; import org.assertj.core.util.Lists; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; / * @author yanfengzhang * @description 获取电影thrift接口分页处理 * @date 2022/3/18 23:32 */ @Slf4j public class GetMoviesByConditionPage extends ThriftInvokePageCommand
{ / * 实际thrift接口(此处只做模拟) * 实际该处应该类似为:private MoviesInfoThriftService.Iface moviesInfoThriftService; */ private MoviesInfoThriftService moviesInfoThriftService; / * 实际分页调用构造器 * * @param moviesConditions 请求查询分页请求 * @param moviesInfoThriftService 实际分页调用电影ThriftService */ public GetMoviesByConditionPage(MoviesConditions moviesConditions, MoviesInfoThriftService moviesInfoThriftService) { super(moviesConditions); this.moviesInfoThriftService = moviesInfoThriftService; } / * 设置实际页号 * * @param moviesConditions 请求查询分页请求 * @param pageNo 页号 */ @Override protected void setPageNo(MoviesConditions moviesConditions, int pageNo) { if (Objects.isNull(moviesConditions)) { return; } moviesConditions.setPageNo(pageNo); } / * 设置实际页大小 * * @param moviesConditions 请求查询分页请求 * @param pageSize 页大小 */ @Override protected void setPageSize(MoviesConditions moviesConditions, int pageSize) { if (Objects.isNull(moviesConditions)) { return; } moviesConditions.setPageSize(pageSize); } / * 获取实际数据大小 * * @param moviesResult 请求查询分页请求 * @return 实际数据大小 */ @Override protected int getResultSize(MoviesResult moviesResult) { if (Objects.isNull(moviesResult)) { return 0; } return moviesResult.getMovieInfos().size(); } / * 实际业务调用thrift接口 * * @param moviesConditions 请求查询分页请求 * @return 实际业务调用thrift接口返回 * @throws Exception 业务异常 */ @Override protected MoviesResult invoke(MoviesConditions moviesConditions) throws Exception { if (Objects.isNull(moviesConditions)) { return null; } return moviesInfoThriftService.getMoviesByConditionPage(moviesConditions); } / * 获取实际业务需要的结果(进行实际的转换) * * @param moviesResult 实际业务调用thrift接口返回 * @return 实际业务需要的电影基本信息 */ @Override protected List
convertThriftResult(MoviesResult moviesResult) { if (Objects.isNull(moviesResult) || CollectionUtils.isEmpty(moviesResult.getMovieInfos())) { return Lists.newArrayList(); } return moviesResult.getMovieInfos().stream().map(movieInfo -> MovieBasicInfo.builder() .id(movieInfo.getId()) .name(movieInfo.getName()) .desc(movieInfo.getDesc()) .director(movieInfo.getDirector()) .actor(movieInfo.getActor()) .duration(movieInfo.getDuration()) .year(movieInfo.getYear()) .score(movieInfo.getScore()).build()).collect(Collectors.toList()); } }
获取电影thrift接口滚动处理
package org.zyf.javabasic.designpatterns.strategy.template.thrift; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections.CollectionUtils; import org.assertj.core.util.Lists; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; / * @author yanfengzhang * @description 获取电影thrift接口滚动处理 * @date 2022/3/20 17:37 */ @Slf4j public class GetMoviesByConditionScroll extends ThriftInvokeScrollCommand
{ / * 实际thrift接口(此处只做模拟) * 实际该处应该类似为:private MoviesInfoThriftService.Iface moviesInfoThriftService; */ private MoviesInfoThriftService moviesInfoThriftService; / * 实际分页调用构造器 * * @param moviesConditions 请求查询分页请求 * @param moviesInfoThriftService 实际分页调用电影ThriftService */ public GetMoviesByConditionScroll(MoviesConditions moviesConditions, MoviesInfoThriftService moviesInfoThriftService) { super(moviesConditions); this.moviesInfoThriftService = moviesInfoThriftService; } / * @param moviesConditions 请求查询分页请求 * @param scrollId 当前滚动ID */ @Override protected void setScrollId(MoviesConditions moviesConditions, Long scrollId) { if (Objects.isNull(moviesConditions)) { return; } moviesConditions.setScrollId(scrollId); } / * @param thriftResult 请求查询分页请求 * @return 下一个滚动ID */ @Override protected Long getNextScrollId(MoviesResult thriftResult) { if (Objects.isNull(thriftResult)) { return 0L; } return thriftResult.getNextScrollId(); } / * 设置实际页大小 * * @param moviesConditions 请求查询分页请求 * @param pageSize 页大小 */ @Override protected void setPageSize(MoviesConditions moviesConditions, int pageSize) { if (Objects.isNull(moviesConditions)) { return; } moviesConditions.setPageSize(pageSize); } / * 获取实际数据大小 * * @param moviesResult 请求查询分页请求 * @return 实际数据大小 */ @Override protected int getResultSize(MoviesResult moviesResult) { if (Objects.isNull(moviesResult)) { return 0; } return moviesResult.getMovieInfos().size(); } / * 实际业务调用thrift接口 * * @param moviesConditions 请求查询分页请求 * @return 实际业务调用thrift接口返回 * @throws Exception 业务异常 */ @Override protected MoviesResult invoke(MoviesConditions moviesConditions) throws Exception { if (Objects.isNull(moviesConditions)) { return null; } return moviesInfoThriftService.getMoviesByConditionScroll(moviesConditions); } / * 获取实际业务需要的结果(进行实际的转换) * * @param moviesResult 实际业务调用thrift接口返回 * @return 实际业务需要的电影基本信息 */ @Override protected List
convertThriftResult(MoviesResult moviesResult) { if (Objects.isNull(moviesResult) || CollectionUtils.isEmpty(moviesResult.getMovieInfos())) { return Lists.newArrayList(); } return moviesResult.getMovieInfos().stream().map(movieInfo -> MovieBasicInfo.builder() .id(movieInfo.getId()) .name(movieInfo.getName()) .desc(movieInfo.getDesc()) .director(movieInfo.getDirector()) .actor(movieInfo.getActor()) .duration(movieInfo.getDuration()) .year(movieInfo.getYear()) .score(movieInfo.getScore()).build()).collect(Collectors.toList()); } }
业务模拟服务
前置准备数据
查询电影资源条件集合
package org.zyf.javabasic.designpatterns.strategy.template.thrift; import lombok.Builder; import lombok.Data; / * @author yanfengzhang * @description 查询电影资源条件集合 * @date 2022/3/18 23:33 */ @Data @Builder public class MoviesConditions { / * 综合排序类型:1-热播榜,2-好评榜,3-新上线,4-爱奇艺自制(热播),5-腾讯自制(热播),6-优酷自制(热播) */ private int sortType; / * 播放类型:1-剧情,2-喜剧,3-动作,4-爱情,5-惊悚,6-犯罪,7-悬疑,8-战争,9-科幻,10-动画,11-恐怖 * 12-家庭,13-传记,14-冒险,15-奇幻,16-武侠,17-历史,18-运动,19-歌舞,20-音乐,21-记录 * 22-伦理,23-西部 */ private int playType; / * 电影产地归类:1-内地,2-中国香港,3-美国,4-欧洲,5-日本,6-韩国,7-英国,8-法国,9-德国,10-泰国 * 11-印度,12-意大利,13-西班牙,14-加拿大,15-澳大利亚,16-中国台湾,17-拉丁美洲,18-越南 * 19-俄罗斯,20-其他 */ private int productPlace; / * 电影源:1-院线,2-蓝光,3-奥斯卡,4-自制电影,5-独播,6-烂片,7-网络电影,8-巨制 */ private int source; / * 付费类型:1-免费,2-付费,3-VIP */ private int paymentType; / * 年份归类:1-2022年,2-2021年,3-2020年,4-2019年,5-20211-2018年 * 6-2000-2010年,7-90年代,8-80年代,9-更早 */ private int years; / * 页号 */ private int pageNo; / * 页大小 */ private int pageSize; / * 当前滚动ID */ private Long scrollId; }
电影资源信息(只是样板)
package org.zyf.javabasic.designpatterns.strategy.template.thrift; import lombok.Builder; import lombok.Data; import java.util.List; / * @author yanfengzhang * @description 电影资源信息(只是样板) * @date 2022/3/18 23:33 */ @Data @Builder public class MovieInfo { / * ID编号 */ private Long id; / * 电影名称 */ private String name; / * 电影简介 */ private String desc; / * 电影导演 */ private String director; / * 电影演员 */ private String actor; / * 电影综合评分 */ private Integer score; / * 电影时长 */ private Integer duration; / * 归属排序:1-热播榜,2-好评榜,3-新上线,4-爱奇艺自制(热播),5-腾讯自制(热播),6-优酷自制(热播) */ private int belong; / * 播放类型:1-剧情,2-喜剧,3-动作,4-爱情,5-惊悚,6-犯罪,7-悬疑,8-战争,9-科幻,10-动画,11-恐怖 * 12-家庭,13-传记,14-冒险,15-奇幻,16-武侠,17-历史,18-运动,19-歌舞,20-音乐,21-记录 * 22-伦理,23-西部 */ private List
playTypes; / * 电影产地归类:1-内地,2-中国香港,3-美国,4-欧洲,5-日本,6-韩国,7-英国,8-法国,9-德国,10-泰国 * 11-印度,12-意大利,13-西班牙,14-加拿大,15-澳大利亚,16-中国台湾,17-拉丁美洲,18-越南 * 19-俄罗斯,20-其他 */ private int productPlace; / * 电影源:1-院线,2-蓝光,3-奥斯卡,4-自制电影,5-独播,6-烂片,7-网络电影,8-巨制 */ private List
sources; / * 付费类型:1-免费,2-付费,3-VIP */ private int paymentType; / * 上映年份 */ private int year; }
基本电影信息
package org.zyf.javabasic.designpatterns.strategy.template.thrift; import lombok.Builder; import lombok.Data; / * @author yanfengzhang * @description 基本电影信息 * @date 2022/3/18 23:35 */ @Data @Builder public class MovieBasicInfo { / * ID编号 */ private Long id; / * 电影名称 */ private String name; / * 电影简介 */ private String desc; / * 电影导演 */ private String director; / * 电影演员 */ private String actor; / * 电影综合评分 */ private Integer score; / * 电影时长 */ private Integer duration; / * 上映年份 */ private int year; }
获取的电影集合
package org.zyf.javabasic.designpatterns.strategy.template.thrift; import lombok.Builder; import lombok.Data; import java.util.List; / * @author yanfengzhang * @description 获取的电影集合 * @date 2022/3/18 23:35 */ @Data @Builder public class MoviesResult { / * 总数目 */ private long total; / * 具体电影信息 */ public List
movieInfos; / * 电影ID集合 */ public List
movieIdList; / * 下一个滚动ID */ private Long nextScrollId; }
具体模拟业务
package org.zyf.javabasic.designpatterns.strategy.template.thrift; import com.google.common.collect.Maps; import lombok.extern.slf4j.Slf4j; import org.assertj.core.util.Lists; import org.springframework.stereotype.Service; import java.util.List; import java.util.Map; import java.util.Random; / * @author yanfengzhang * @description 模拟实际电影thrift接口 * @date 2022/3/18 23:39 */ @Service @Slf4j public class MoviesInfoThriftService { private static Map
descWords = Maps.newHashMap(); static { descWords.put(1, "人生若无悔,那该多无趣啊。——《一代宗师》"); descWords.put(2, "冬天冷,是因为为了让我们懂得周围人的温暖,是多么的珍贵。——《熔炉》"); descWords.put(3, "你要尽全力保护你的梦想。那些嘲笑你梦想的人,他们注定失败,他们想把你变成和他们一样。我坚信,只要心中有梦想,我就会与众不同。你也是。——《当幸福来敲门》"); descWords.put(5, "成功的含义不在于得到什么,而是在于你从那个奋斗的起点走了多远。——《心灵捕手》"); descWords.put(6, "让朋友低估你的优点,让敌人高估你的缺点。——《教父》"); descWords.put(7, "所有大人都曾是小孩,虽然只有少数人记得。——《小王子》"); descWords.put(8, "记住,希望是好事,也许是人间至善,而美好的事物永不消逝。——《肖申克的救赎》"); descWords.put(9, "当你不能再拥有的时候,唯一可以做的,就是令自己不要忘记。——《东邪西毒》"); descWords.put(10, "我们要学会珍惜我们生活的每一天,因为,这每一天的开始,都将是我们余下生命之中的第一天。除非我们即将死去。——《美国美人》"); } public MoviesResult getMoviesByConditionPage(MoviesConditions moviesConditions) { if (!checkPosInteger(moviesConditions.getPageSize())) { return null; } List
movieInfos = Lists.newArrayList(); List
movieIdList = Lists.newArrayList(); /*模拟数据,返回第一页数据和指定的页面大小*/ if (moviesConditions.getPageNo() == 1) { for (int i = 1; i <= moviesConditions.getPageSize(); i++) { movieIdList.add((long) i); movieInfos.add(MovieInfo.builder() .id((long) i) .name("张彦峰喜欢的电影" + i) .desc(descWords.get(i)) .actor("张彦峰") .director("张彦峰") .belong(2) .score(new Random().nextInt(10) % (10 - i + 1) + i).build()); } } if (moviesConditions.getPageNo() == 2) { for (int i = 6; i <= moviesConditions.getPageSize() + 5; i++) { movieIdList.add((long) i); movieInfos.add(MovieInfo.builder() .id((long) i) .name("张彦峰喜欢的电影" + i) .desc(descWords.get(i)) .actor("张彦峰") .director("张彦峰") .belong(2) .score(new Random().nextInt(10) % (10 - i + 1) + i).build()); } } return MoviesResult.builder().total(5).movieIdList(movieIdList).movieInfos(movieInfos).build(); } public MoviesResult getMoviesByConditionScroll(MoviesConditions moviesConditions) { if (!checkPosInteger(moviesConditions.getPageSize()) || moviesConditions.getScrollId() < 0) { return null; } List
movieInfos = Lists.newArrayList(); List
movieIdList = Lists.newArrayList(); /*模拟数据,返回第一页数据和指定的页面大小*/ if (moviesConditions.getScrollId() == 0) { for (int i = 1; i <= moviesConditions.getPageSize(); i++) { movieIdList.add((long) i); movieInfos.add(MovieInfo.builder() .id((long) i) .name("张彦峰喜欢的电影" + i) .desc(descWords.get(i)) .actor("张彦峰") .director("张彦峰") .belong(2) .score(new Random().nextInt(10) % (10 - i + 1) + i).build()); } return MoviesResult.builder().total(5).movieIdList(movieIdList).movieInfos(movieInfos).nextScrollId(1L).build(); } if (moviesConditions.getScrollId() == 1) { for (int i = 6; i <= moviesConditions.getPageSize() + 5; i++) { movieIdList.add((long) i); movieInfos.add(MovieInfo.builder() .id((long) i) .name("张彦峰喜欢的电影" + i) .desc(descWords.get(i)) .actor("张彦峰") .director("张彦峰") .belong(2) .score(new Random().nextInt(10) % (10 - i + 1) + i).build()); } } return MoviesResult.builder().total(5).movieIdList(movieIdList).movieInfos(movieInfos).nextScrollId(-1L).build(); } / * ID信息合法性:必须为正整数 * * @param id id信息 * @return true-合法;false-非法 */ public static boolean checkPosInteger(Integer id) { return null != id && id > 0L; } }
测试及展示
测试代码
package org.zyf.javabasic.designpatterns.strategy.template.thrift; import lombok.extern.slf4j.Slf4j; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import org.zyf.javabasic.ZYFApplication; import java.util.List; / * @author yanfengzhang * @description 测试按页获取远程数据 * @date 2022/3/20 17:21 */ @RunWith(SpringRunner.class) @SpringBootTest(classes = ZYFApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @Slf4j public class ThriftInvokeCommandTest { @Autowired private MoviesInfoThriftService moviesInfoThriftService; @Test public void testGetMoviesByCondition() { log.info("测试分页获取数据:"); MoviesConditions moviesConditions1 = MoviesConditions.builder() .pageNo(1) .pageSize(2) .build(); List movieBasicInfos1 = new GetMoviesByConditionPage(moviesConditions1, moviesInfoThriftService) .setPageSize(5) .setSleepMs(100) .getResult(); log.info("按实际条件返回的基本结果为:{}", movieBasicInfos1); log.info("测试滚动获取数据:"); MoviesConditions moviesConditions2 = MoviesConditions.builder() .pageNo(1) .pageSize(2) .scrollId(0L) .build(); List
movieBasicInfos2 = new GetMoviesByConditionScroll(moviesConditions2, moviesInfoThriftService) .setPageSize(5) .setSleepMs(100) .getResult(); log.info("按实际条件返回的基本结果为:{}", movieBasicInfos2); } }
业务处理统一相关处理模版
可以根据业务要求也安排一些常规的模版流程进行处理,同时对于其基本的操作而言,可以在细化为三种类型:支持SpringValidator的业务处理模版、没有入参调用的业务处理模版、没有返回参数调用的业务处理模版,主要代码如下:
基本的业务处理模版BizTemplate
package org.zyf.javabasic.designpatterns.template.biz; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.List; / * @author yanfengzhang * @description 基本的业务处理模版 * @date 2022/4/24 23:10 */ public abstract class BizTemplate { protected BizResponse
response = BizResponse.success(); / * 请求参数 */ protected Command command; / * 请求结果 */ protected Result result; / * 方法名称,用于记录日志 */ protected String methodName; / * 日志 */ protected Logger templateLogger = LoggerFactory.getLogger(BizTemplate.class); / * 验证器集合 */ protected List
validationList = new ArrayList<>(10); public BizTemplate(Command command, Result result) { this.methodName = this.getClass().getName(); this.command = command; this.result = result; } public BizTemplate(Result result) { this.methodName = this.getClass().getName(); this.command = (Command) new BaseCommand(); this.result = result; } public BizTemplate(Command command) { this.methodName = this.getClass().getName(); this.command = command; this.result = (Result) new BaseResult(); } / * check template * * @return */ protected boolean templateValidate() { if (command == null || result == null || StringUtils.isBlank(methodName) || templateLogger == null) { return false; } return true; } / * before * you can write the logic code that execute before the primay business logic * for example validate paramaters, check preposed data and so on */ public abstract void doBefore(); / * primay business logic */ public abstract void doBiz() throws Exception; public void doException() throws Exception { } / * after * you can write the logic code that execute after the primay business logic */ public void doAfter() { } / * add validation * * @param check * @param msg */ public void addValidation(boolean check, String msg) { this.validationList.add(new Validation(check, msg)); } / * execute validation * * @param msg, result * @param flag, record if the result is false, default is false */ protected void doValidate(StringBuilder msg, boolean flag) { if (CollectionUtils.isNotEmpty(validationList)) { for (Validation validation : validationList) { if (!validation.check) { msg.append(" "); msg.append(validation.msg); msg.append(" "); flag = true; } } } if (flag) { throw new ZYFServerException(ErrorCode.PARAMS_CHECK_FAILED.getCode(), String.format(ErrorCode.PARAMS_CHECK_FAILED.getMsg(), msg.toString())); } } / * execute * * @return */ public BizResponse
execute() throws Exception { long threadId = Thread.currentThread().getId(); if (!this.templateValidate()) { throw new IllegalArgumentException("the usage of WebControllerBizTemplate is illegal"); } this.doBefore(); this.doValidate(new StringBuilder(), false); long startTime = System.currentTimeMillis(); try { this.doBiz(); } catch (Exception e) { this.doException(); throw e; } this.doAfter(); response.setData(result); return response; } / * validator * * @author sunwei */ class Validation { boolean check; String msg; public Validation(boolean check, String msg) { super(); this.check = check; this.msg = msg; } } public String getMethodName() { return methodName; } public void setMethodName(String methodName) { this.methodName = methodName; } public Logger getTemplateLogger() { return templateLogger; } public void setTemplateLogger(Logger templateLogger) { this.templateLogger = templateLogger; } }
支持SpringValidator的业务处理模版
package org.zyf.javabasic.designpatterns.template.biz; import org.springframework.validation.BindingResult; import org.springframework.validation.ObjectError; / * @author yanfengzhang * @description 支持Spring Validator * @date 2022/4/24 23:22 */ public abstract class BizTemplateWithSpringValidator extends BizTemplate
{ private BindingResult br; public BizTemplateWithSpringValidator(Command command, Result result, BindingResult br) { super(command, result); this.br = br; } @Override protected void doValidate(StringBuilder msg, boolean flag) { if (br.hasErrors()) { for (ObjectError error : br.getAllErrors()) { msg.append(" "); msg.append(error.getDefaultMessage()); msg.append(" "); } } flag = br.hasErrors(); super.doValidate(msg, flag); } }
没有入参调用的业务处理模版
package org.zyf.javabasic.designpatterns.template.biz; import org.apache.commons.lang3.StringUtils; / * @author yanfengzhang * @description 没有入参的调用 * @date 2022/4/24 23:23 */ public abstract class BizWithoutCommandTemplate
extends BizTemplate
{ public BizWithoutCommandTemplate(Result result) { super(new BaseCommand(), result); } @Override protected boolean templateValidate() { if (result == null || StringUtils.isBlank(methodName) || templateLogger == null) { return false; } return true; } }
没有返回参数调用的业务处理模版
package org.zyf.javabasic.designpatterns.template.biz; import org.apache.commons.lang3.StringUtils; / * @author yanfengzhang * @description 没有入参的调用 * @date 2022/4/24 23:23 */ public abstract class BizWithoutCommandTemplate
extends BizTemplate
{ public BizWithoutCommandTemplate(Result result) { super(new BaseCommand(), result); } @Override protected boolean templateValidate() { if (result == null || StringUtils.isBlank(methodName) || templateLogger == null) { return false; } return true; } }
业务使用举例
package org.zyf.javabasic.designpatterns.template.biz; import com.google.common.collect.Lists; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections.CollectionUtils; import org.springframework.stereotype.Controller; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import javax.annotation.Nullable; import java.util.List; / * @author yanfengzhang * @description * @date 2022/4/24 23:32 */ @Controller @RequestMapping("/org/zyf") @Slf4j public class ZYFBizTemplateController { @RequestMapping("r/listSubBrand") @ResponseBody public BizResponse
listSubBrand(ListSubBrandCommand command, final BindingResult br) throws Exception { return new BizTemplateWithSpringValidator
(command, new ListSubBrandResult(), br) { void brandValidator() { /*根据接口查看传入品牌是否是有效品牌*/ List
illegalBrands = Lists.newArrayList(); if (CollectionUtils.isEmpty(command.getBrandIds())) { return; } /*模拟调用返回无效品牌*/ illegalBrands.addAll(command.getBrandIds()); this.addValidation(CollectionUtils.isEmpty(illegalBrands), String.format("存在不合法品牌信息[%s]", command.getBrandIds())); } @Override public void doBefore() { /*1.对入参进行调整或默认填充,比方说command的基本必要参数未填写可默认指定处理*/ /*2.对基本参数进行检查等*/ this.addValidation(command.getParentId() > 0, "parentId必须大于0"); this.brandValidator(); } @Override public void doBiz() throws Exception { List
brandList = Lists.newArrayList(); result.convert2CruxBrand(brandList); } }.execute(); } @RequestMapping(value = "r/getStandardCategoryTagValueList") @ResponseBody public BizResponse
getStandardCategoryTagValueList() throws Exception { return new BizWithoutCommandTemplate
(new GetSpCategoryMetaAttrValueListResult()) { @Override public void doBefore() { } @Override public void doBiz() throws Exception { /*模拟调用*/ //CRUXStandardProductCategoryMetaAttrValueListResult thriftResult = cruxStandardProductCategoryThriftService.getCategoryMetaAttrValueListByMetacode(MetaAttrEnum.STANDARD_CATEGORY_TAG.getCode()); CRUXStandardProductCategoryMetaAttrValueListResult thriftResult = new CRUXStandardProductCategoryMetaAttrValueListResult(); if (thriftResult == null) { throw new ZYFServerException(ErrorCode.DATA_NOT_FOUND.getCode(), ErrorCode.DATA_NOT_FOUND.getMsg()); } if (CollectionUtils.isEmpty(thriftResult.getCategoryMetaAttrValueList())) { return; } for (ScSpCategoryMetaAttrValue value : thriftResult.getCategoryMetaAttrValueList()) { result.getSpCategoryMetaAttrValueList().add(new ScSpCategoryMetaAttrValueVo()); } } }.execute(); } @RequestMapping(value = "strategy/w/strategyDeploy", method = RequestMethod.POST) @ResponseBody public BizResponse
batchDeployStrategy(@RequestBody BatchDeployStrategyCommand command) throws Exception { return new BizWithoutResultTemplate
(command) { @Override public void doBefore() { addValidation(command.getProcessBizType() != null, "processBizType不能为null,请参考com.sankuai.meituan.tsp.product.bizconsistency.enums.BizTypeEnum"); addValidation(command.getProcessDataType() != null, "processDataType不能为null,请参考com.sankuai.meituan.tsp.product.bizconsistency.enums.DataTypeEnum"); addValidation(command.getOperation() != null, "operation不能为null,1-发布策略 2-撤销策略"); } @Override public void doBiz() throws Exception { BizTypeEnum bizTypeEnum = BizTypeEnum.findByType(command.getProcessBizType()); if (bizTypeEnum == null) { throw new IllegalArgumentException(String.format("%d是非法的processBizType", command.getProcessBizType())); } DataTypeEnum dataTypeEnum = DataTypeEnum.findByType(command.getProcessDataType()); if (dataTypeEnum == null) { throw new IllegalArgumentException(String.format("%d是非法的processDataType", command.getProcessDataType())); } if (command.isDeploy()) { // productBcpService.batchDeployStrategy(bizTypeEnum, dataTypeEnum, command.getStrategyIdList()); } else if (command.isUndeploy()) { // productBcpService.batchUndeployStrategy(bizTypeEnum, dataTypeEnum, command.getStrategyIdList()); } } }.execute(); } @AllArgsConstructor @Getter public enum BizTypeEnum { / * 业务模型类型 */ PRODUCT_SUPPLY_SPU(1, "spuId", "商品供给业务SPU"), PRODUCT_SUPPLY_SKU(2, "skuId", "商品供给业务SKU"), PRODUCT_SUPPLY_TAG(3, "tagId", "商品供给业务TAG"), ; private int type; private String bizId; private String desc; @Nullable public static BizTypeEnum findByType(int type) { for (BizTypeEnum each : BizTypeEnum.values()) { if (each.getType() == type) { return each; } } return null; } } @AllArgsConstructor @Getter public enum DataTypeEnum { / * 不一致问题数据类型 */ MYSQL_ONLY(1, "仅Mysql"), MYSQL_AND_ES(2, "Mysql和ES"), MYSQL_AND_TAIR(3, "Mysql和Tair"), MYSQL_AND_REDIS(4, "Mysql和Redis"), MYSQL_AND_MTSEARCH(5, "Mysql和大搜"), ; private int type; private String desc; public static DataTypeEnum findByType(int type) { for (DataTypeEnum each : DataTypeEnum.values()) { if (each.getType() == type) { return each; } } return null; } } }
发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/211588.html原文链接:https://javaforall.net
