最近在工作中遇到了代码中循环依赖的问题,记录下解决方案。
1.问题原因
这里涉及到两个类YgReplenishmentAppService(补货单处理)和YgPurchaseOrderAppService(采购单处理)
我们看下类中的代码:
@Slf4j
@Service
public class YgReplenishmentAppService {
private YgPurchaseOrderAppService ygPurchaseOrderAppService;
private void getHotelEstimateNumber(Set<String> skuSet, Map<Long, Map<String, BigDecimal>> hotelEstimateNumberMap, Long hotelId) {
// 预计可用数量
List<String> skuNoList = new ArrayList<>(skuSet);
List<InventoryForecastItem> inventoryForecastItemList = inventoryAppService.queryInventoryForecastByHotel(skuNoList, hotelId);
// 补货申请
inventoryForecastItemList.addAll(queryReplenishmentItem(hotelId, skuNoList));
// 采购订单
inventoryForecastItemList.addAll(ygPurchaseOrderAppService.queryPurchaseOrderItem(hotelId, skuNoList));
// 填充运营物品分类编码
for(InventoryForecastItem forecastItem:inventoryForecastItemList){
StatsGroupInfoVO statsGroupInfoBySkuNo = statsGroupInfoRepostitory.getStatsGroupInfoBySkuNo(forecastItem.getSkuNo());
if(ObjectUtils.isEmpty(statsGroupInfoBySkuNo)){
log.info("未获取到商品{}的运营物品分类信息,将不做补货处理",forecastItem.getSkuNo());
continue;
}
forecastItem.setGoodsClassifyCode(statsGroupInfoBySkuNo.getGroupInfoCode());
forecastItem.setGoodsClassifyName(statsGroupInfoBySkuNo.getValueDesc());
}
//剔除没有运营物品编码的商品
List<InventoryForecastItem> activeInventoryForecastItemList = inventoryForecastItemList.stream().filter(item -> item.getGoodsClassifyCode() != null).collect(Collectors.toList());
//按照运营物品分组
Map<String, List<InventoryForecastItem>> skuInventoryForecastMap = activeInventoryForecastItemList.stream().collect(Collectors.groupingBy(InventoryForecastItem::getGoodsClassifyCode));
if (skuInventoryForecastMap != null) {
Map<String,BigDecimal> estimateNumberMap = new HashMap<>();
for(String classifyCode:skuInventoryForecastMap.keySet()){
BigDecimal estimateNumber = skuInventoryForecastMap.get(classifyCode).stream().map(InventoryForecastItem::getNumber).reduce(BigDecimal.ZERO, BigDecimal::add);
estimateNumberMap.put(classifyCode,estimateNumber);
}
hotelEstimateNumberMap.put(hotelId,estimateNumberMap);
}
}
}
@Slf4j
@Service
public class YgPurchaseOrderAppService {
@Autowired
private YgReplenishmentAppService ygReplenishmentAppService;
/**
* 创建或者编辑易购采购订单
*
* @param dto 创建入参
* @throws BizsException 异常
*/
@Transactional(rollbackFor = Exception.class)
public void createOrUpdateYgPurchaseOrder(YgPurchaseOrderInboundDTO dto) throws BizsException {
// 查询对应的酒店信息
YgPurchaseOrderContextDTO ctxDto = buildContextDto(dto);
// 新建或者更新已存在的旧数据
YgPurchaseOrder model = ygPurchaseOrderRepository.queryWholeByOrderNo(dto.getOrderNo());
if (null == model) {
model = YgPurchaseOrder.create(dto, ctxDto);
ygPurchaseOrderRepository.insert(model);
} else {
model.update(dto, ctxDto);
boolean result = ygPurchaseOrderRepository.update(model);
if (!result) {
throw new BizsException(ResultEnum.DOCUMENT_UPDATE_ERROR);
}
}
// 调用补货服务方法, 对补货记录进行回写
try {
YgPurchaseOrderSyncDTO syncDto = BeanUtil.mapByJson(model, YgPurchaseOrderSyncDTO.class);
ygReplenishmentAppService.syncReplenishmentByYgPurchaseOrderSyncDto(syncDto);
} catch (Exception e) {
log.error("易购采购订单回写失敗, 订单:[{}] 异常:{}", model, e);
}
}
}
可以看到在YgReplenishmentAppService中调用了ygPurchaseOrderAppService中的方法,而在ygPurchaseOrderAppService中同样调用了YgReplenishmentAppService中的方法,这样就造成了两个类存在循环依赖的问题。
2.解决方案
思路是将相互依赖的两个类进行解耦操作。可以引入第三方的方法去达到目的。这里是引入事件的处理模式,当收到采购订单时触发补货记录单的回写事件,从而避免在ygPurchaseOrderAppService中直接调用补货处理类(YgReplenishmentAppService)
具体看如下代码:
@Slf4j
@Service
public class YgPurchaseOrderAppService {
@Autowired
private ApplicationContext applicationContext;
/**
* 创建或者编辑易购采购订单
*
* @param dto 创建入参
* @throws BizsException 异常
*/
@Transactional(rollbackFor = Exception.class)
public void createOrUpdateYgPurchaseOrder(YgPurchaseOrderInboundDTO dto) throws BizsException {
// 查询对应的酒店信息
YgPurchaseOrderContextDTO ctxDto = buildContextDto(dto);
// 新建或者更新已存在的旧数据
YgPurchaseOrder model = ygPurchaseOrderRepository.queryWholeByOrderNo(dto.getOrderNo());
if (null == model) {
model = YgPurchaseOrder.create(dto, ctxDto);
ygPurchaseOrderRepository.insert(model);
} else {
model.update(dto, ctxDto);
boolean result = ygPurchaseOrderRepository.update(model);
if (!result) {
throw new BizsException(ResultEnum.DOCUMENT_UPDATE_ERROR);
}
}
//这里将数据封装,并触发事件,让第三方处理,不再直接调用ygPurchaseOrderAppService
YgOrderCreatedOrUpdated ygOrderCreatedOrUpdated = new YgOrderCreatedOrUpdated();
ygOrderCreatedOrUpdated.setYgPurchaseOrder(model);
applicationContext.publishEvent(ygOrderCreatedOrUpdated);
}
}
上面提交了事件之后需要监听事件,去处理余下逻辑:
@Slf4j
@Component
public class YgOrderCreatedOrUpdatedListener {
private YgReplenishmentAppService ygReplenishmentAppService;
@TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT)
public void onApplicationEvent(YgOrderCreatedOrUpdated ygOrderCreatedOrUpdated) {
// 调用补货服务方法, 对补货记录进行回写
YgPurchaseOrder model = ygOrderCreatedOrUpdated.getYgPurchaseOrder();
try {
YgPurchaseOrderSyncDTO syncDto = BeanUtil.mapByDozer(model, YgPurchaseOrderSyncDTO.class);
ygReplenishmentAppService.syncReplenishmentByYgPurchaseOrderSyncDto(syncDto);
} catch (Exception e) {
log.error("易购采购订单回写失敗, 订单:[{}] 异常:{}", model, e);
}
}
@Autowired
public void setYgReplenishmentAppService(YgReplenishmentAppService ygReplenishmentAppService) {
this.ygReplenishmentAppService = ygReplenishmentAppService;
}
}
这样就可以成功解耦采购单和补货处理之间的依赖关系,后面遇到类似问题触类旁通。