一、背景
在项目开发中,我们遇到了一个需要处理多种类型查询的场景。每种类型的查询逻辑相似,但又存在细微差别。为了提高代码的复用性和可维护性,我们决定对多线程查询逻辑进行抽象化处理。
二、问题描述
-
重复代码:每种查询类型都有一套类似的多线程查询逻辑,导致代码冗余。
-
扩展性差:新增查询类型时,需要重复编写类似的逻辑,增加了开发成本和出错的概率。
-
维护困难:由于代码重复,修改一处逻辑时需要在多个地方同步修改,容易遗漏。
三、优化思路
-
提取公共逻辑:将多线程查询的公共逻辑提取到一个通用方法中。
-
使用函数式接口:通过函数式接口将查询逻辑作为参数传入通用方法,避免重复代码。
-
策略模式:利用策略模式,将每种查询类型封装为一个策略类,继承自通用的父类。
四、实现步骤
1. 定义通用查询逻辑
在 AbstractFormFillHandler
类中,定义一个通用的多线程查询方法 queryData
,并使用函数式接口 QueryFunction
来传入具体的查询逻辑。
@Slf4j
@Service
public abstract class AbstractFormFillHandler implements FormFillHandler {
@Autowired
protected FormFillConfService fillConfService;
@Autowired
protected FormFillDataQueryService dataQueryService;
/**
* 通用的多线程查询方法
*
* @param request 查询请求
* @param queryFunction 查询逻辑的函数
* @return 查询结果
*/
protected List<FormFillResponse> queryData(FormFillRequest request, QueryFunction queryFunction) {
List<FormFillConf> fillConfList = fillConfService.selectConfigList(request);
List<CompletableFuture<FormFillResponse>> futures = new ArrayList<>();
for (FormFillConf config : fillConfList) {
log.info("数据库配置信息查询入参{}", JSONUtil.toJsonStr(request));
log.info("表单查询入参{}", JSONUtil.toJsonStr(config));
int controlSeqId = config.getControlSeqId() == null ? 99999 : config.getControlSeqId();
FormFillConvert formFillConvert = new FormFillConvert();
BeanUtils.copyProperties(request, formFillConvert);
formFillConvert.setControlSeqId(controlSeqId);
formFillConvert.setColName(config.getColName());
formFillConvert.setTableName(config.getTableName());
// 使用 CompletableFuture 异步查询
CompletableFuture<FormFillResponse> future = CompletableFuture.supplyAsync(() -> {
FormFillResponse response = new FormFillResponse();
response.setTitle(config.getControlName());
response.setControlSeqId(controlSeqId);
try {
// 尝试从主表查询
List<FormFillItemResult> itemResults = queryFunction.apply(formFillConvert);
response.setResultList(itemResults);
} catch (Exception e) {
log.error("主表查询失败,尝试从备份表查询", e);
// 如果主表查询失败,尝试从备份表查询
formFillConvert.setTableName(config.getBakTableName()); // 设置备份表名
try {
List<FormFillItemResult> bakItemResults = queryFunction.apply(formFillConvert);
response.setResultList(bakItemResults);
} catch (Exception bakException) {
log.error("备份表查询失败,忽略此次失败结果", bakException);
// 忽略此次失败结果,返回空结果
response.setResultList(Collections.emptyList());
}
}
return response;
});
futures.add(future);
}
// 等待所有异步任务完成,并收集结果
return futures.stream()
.map(CompletableFuture::join) // 等待每个异步任务完成
.filter(response -> response.getResultList() != null && !response.getResultList().isEmpty()) // 过滤掉空结果
.sorted(Comparator.comparingInt(FormFillResponse::getControlSeqId)) // 按照 ControlSeqId 排序
.collect(Collectors.toList());
}
/**
* 查询逻辑的函数接口
*/
@FunctionalInterface
public interface QueryFunction {
List<FormFillItemResult> apply(FormFillConvert formFillConvert) throws Exception;
}
}
2. 创建具体策略类
根据不同的查询类型,创建具体的策略类,并在其中调用通用查询方法。
单选策略类
@Slf4j
@Service
public class SingleStrategy extends AbstractFormFillHandler {
@Override
public ControlTypeEnum getControlTypeEnum() {
return ControlTypeEnum.RADIO;
}
@Override
public List<FormFillResponse> getQueryData(FormFillRequest request) {
return queryData(request, formFillConvert -> dataQueryService.selectSingleResultList(formFillConvert));
}
}
多选策略类
@Slf4j
@Service
public class MultipleStrategy extends AbstractFormFillHandler {
@Override
public ControlTypeEnum getControlTypeEnum() {
return ControlTypeEnum.CHECK;
}
@Override
public List<FormFillResponse> getQueryData(FormFillRequest request) {
return queryData(request, formFillConvert -> dataQueryService.selectMultipleResultList(formFillConvert));
}
}
级联选择器策略类
@Slf4j
@Service
public class CascaderStrategy extends AbstractFormFillHandler {
@Override
public ControlTypeEnum getControlTypeEnum() {
return ControlTypeEnum.CASCADER;
}
@Override
public List<FormFillResponse> getQueryData(FormFillRequest request) {
return queryData(request, formFillConvert -> dataQueryService.selectSingleResultList(formFillConvert));
}
}
3. 配置工厂类
在工厂类 FormFillQueryFactory
中,注册所有策略类,以便根据不同的查询类型动态选择对应的策略。
@Slf4j
@Component
public class FormFillQueryFactory {
private final Map<ControlTypeEnum, FormFillHandler> queryHandlerMaps = Maps.newHashMap();
@Autowired
public FormFillQueryFactory(List<FormFillHandler> handlerList) {
for (FormFillHandler handler : handlerList) {
queryHandlerMaps.put(handler.getControlTypeEnum(), handler);
}
}
public List<FormFillResponse> getQueryData(FormFillRequest request) {
checkExportType(request);
FormFillHandler formFillHandler = queryHandlerMaps.get(ControlTypeEnum.getByCode(request.getControlType()));
return formFillHandler.getQueryData(request);
}
private void checkExportType(FormFillRequest request) {
log.info("查询请求入参:{}", JSONUtil.toJsonStr(request));
if (ControlTypeEnum.getByCode(request.getControlType()) == null) {
log.error("查询类型不存在{}", request.getControlType());
throw new BusinessException("查询类型不存在");
}
if (!queryHandlerMaps.containsKey(ControlTypeEnum.getByCode(request.getControlType()))) {
log.error("查询类型不支持{}", request.getControlType());
throw new BusinessException("查询类型不支持");
}
}
}
五、优化效果
-
减少重复代码:通过提取公共逻辑,避免了在每个策略类中重复编写类似的多线程查询逻辑。
-
提高扩展性:新增查询类型时,只需添加一个新的策略类并实现对应的查询逻辑,无需修改现有代码。
-
增强可维护性:公共逻辑集中管理,修改一处逻辑即可在所有策略类中生效,降低了维护成本。
六、总结
通过将多线程查询逻辑抽象化并结合策略模式,我们成功优化了代码结构,提高了代码的复用性和可维护性。这种方法不仅适用于当前项目,还可以推广到其他类似的多类型查询场景中。