spring事件解耦写法优化
1.概念
spring事件发布,是设计模式中观察者模式的实现,观察者模式建立一种对象与对象之间的关系,当一个目标对象发生改变时,由中间者通知一个或多个对象做出反应。一个观察目标可以对应多个观察者。而且这些观察者之间没有相互联系,可以根据需要增加和删除观察者,使得系统更易于扩展。达到代码解耦的目的。
2.背景
以下是当前项目中对spring事件发布的写法。
// 更新借阅单状态
lendingReceiptDao.updateById(lendingReceipt);
// 发布借阅日志记录事件
publisher.publishEvent(new LendingReceiptLogEvent(LendingReceiptLogDto.builder()
.lendingReceiptId(lendingReceipt.getLendingReceiptId())
.opeBehavior(BizOpeBehaviorEnum.FOUND)
.creatorName(userContext.getCurrentUserName())
.creatorId(userContext.getCurrentUseId())
.content("找回:" + reqVo.getRemark())
.build()));
//中间逻辑省略....
// 更新色卡状态
colorCardDao.updateById(colorCard);
// 发布色卡日志记录事件
publisher.publishEvent(newColorCardLogEvent(Collections.singletonList(ColorCardLogDto
.builder()
.colorCardId(colorCard.getColorCardId())
.creatorName(userContext.getCurrentUserName())
.creatorId(userContext.getCurrentUseId())
.opeBehavior(BizOpeBehaviorEnum.FOUND)
.content(reqVo.getRemark())
.businessSegment(BizSegmentEnum.STORE)
.storeId(reqVo.getCurrentStoreId())
.warehouseId(reqVo.getCurrentStoreId())
.warehouseType(WarehouseTypeEnum.STORE.name())
.build())));
// 刷新库存统计事件
publisher.publishEvent(new FlushInventoryEvent(InventoryDto.builder()
.reviserName(userContext.getCurrentUserName())
.reviserId(userContext.getCurrentUseId())
.inventoryIds(Collections.singleton(colorCard.getInventoryId()))
.build()));
2.1当前写法可能存在以下问题
- 业务操作之后发布事件,发布事件代码与业务代码交叉,很难辨认中间是否遗漏事件的发布。
- 事件传递参数过多,非影响性能情况下,建议只传必要参数。不然发送事件代码篇幅过长,影响业务代码的阅读。
- 发布多个事件,多个事件也会导致发送的代码过长。
3.优化方案(伪代码实现)
- 通过切面在dao层后置操作做事件发布,例如新增日志操作记录。这种前提是因为数据库增删改的时候引起的事件,且不适用jpa等泛型操作。目前也仅能做日志相关操作,因为持久层一般只做数据库相关的操作,设计业务的操作不应该放到该层中。
@PublishLogEventAfter
public void insert(LendingReceiptEntity lendingReceipt) {
AssertUtil.isFalse(BeanUtil.isAllPropertyNull(lendingReceipt), "参数非法!");
lendingReceiptMapper.insert(lendingReceipt);
}
@Component
@Aspect
public class PublishLogEventAfterAspect {
@Around(value = "@annotation(publishLogEventAfter)")
public void doServiceBefore(ProceedingJoinPoint pjp,PublishLogEventAfter publishLogEventAfter) {
pjp.proceed();
//获取参数
Object[] args = pjp.getArgs();
// 记录借阅日志
publisher.publishEvent(new LendingReceiptLogEvent(args));
}
- 通过helper层,将多个事件发布操作放在helper里面,由helper统一控制。
@Component
public class LendingEventPublishHelper {
@Autowire
ApplicationEventPublisher publisher;
public void publish(Object... args) {
// 记录借阅日志
publisher.publishEvent(new LendingReceiptLogEvent(args));
// 刷新借阅统计数据
publisher.publishEvent(new LendingStatsEvent());
// 刷新库存统计
publisher.publishEvent(new FlushInventoryEvent());
}
- 只发送一个业务事件,通过实现不同接口,使被对应的监听器消费。
public class LendingEvent extends ApplicationEvent implements FlushEvent, FlushStatEvent {
/**
* Create a new {@code ApplicationEvent}.
*
* @param source the object on which the event initially occurred or with
* which the event is associated (never {@code null})
*/
public LendingEvent(Object source) {
super(source);
}
}
@EventListener(classes = FlushEvent.class)
public void flush(FlushEvent flushEvent) {
System.out.println("FlushEvent事件处理中");
}
@EventListener(classes = FlushStatEvent.class)
public void flush(FlushStatEvent flushStatEvent) {
System.out.println("FlushStatEvent事件处理中");
}
@Test
public void testEvent() {
publisher.publishEvent(new LendingEvent(this));
}
打印结果
FlushStatEvent事件处理中
FlushEvent事件处理中