开发实战-我用Spring的事件监听机制实现了模块的解耦

8 篇文章 0 订阅
1 篇文章 0 订阅

 

背景

这里我们有一个需求:

当用户支付成功时,需要修改订单状态;短信通知用户;通知仓库发货

原始解决方法

你首先想到的肯定是这样

public void paySuccess(String orderId) {
    if (StringUtils.isNotBlank(orderId)) {
        //1.修改订单状态
        //2.发送短信通知用户
        //3.通知仓库发货
    }
}

在支付成功的方法里面调用修改订单的方法,调用短信通知用户的方法,调用仓库发货的方法。完事了,你觉得很简单嘛。

但是,产品经理说,我要改需求了,不止要短信通知,我还要微信通知。这个还是简单。

if (StringUtils.isNotBlank(orderId)) {
    //1.修改订单状态
    //2.发送短信通知用户
    //3.通知仓库发货
    //4.微信通知
}

过了一天产品经理又来加需求了,我还要可以QQ通知,这也不难。

if (StringUtils.isNotBlank(orderId)) {
    //1.修改订单状态
    //2.发送短信通知用户
    //3.通知仓库发货
    //4.微信通知
    //5.QQ通知
}

过了一个月,产品经理又来了:“给我加个功能,我要支付成功后还可以发放优惠券,还要发放积分...”

需求没完没了,但这时你已经忘了支付成功的代码写在哪里了。

终于,你找到了,开始编写。突然,你意识到,不对呀,这个方法越来越臃肿了。而且每次还要来修改这个支付成功的方法,万一修改错误怎么办。

你还意识到一个问题,这些功能都是同步的,万一我调用微信通知的功能失败,难道就不能QQ通知,不能发放优惠券了么?还要全部都回滚。太不合常理了。

你苦思冥想,了解到了一个事件监听机制的方法,可以异步解耦,不正适合这个场景么。说干就干,代码重构走起。

事件监听解决方法

这里我就不说什么是事件监听机制了,概念百度一大把,我将从实际的例子说起,让你彻底理解这个机制是做什么的,什么时候用它。

我们先来定义一个支付事件类

/**
 * Description: 支付事件
 *
 * @author Lvshen
 * @version 1.0
 * @date: 2020-8-28 13:56
 * @since JDK 1.8
 */
public class PayEvent extends ApplicationEvent {
    //订单id
    private String orderId;

    public String getOrderId() {
        return orderId;
    }

    public void setOrderId(String orderId) {
        this.orderId = orderId;
    }

    public PayEvent(Object source,String orderId) {
        super(source);
        this.orderId = orderId;
    }
}

支付服务类这么写

/**
 * Description:支付服务
 *
 * @author Lvshen
 * @version 1.0
 * @date: 2020-8-28 14:02
 * @since JDK 1.8
 */
@Component
public class PayService {

    @Autowired
    public ApplicationEventPublisher applicationEventPublisher;

    /**
     * 支付成功后,发布事件
     * @param orderId
     */
    public void paySuccess(String orderId) {
        if (StringUtils.isNotBlank(orderId)) {
            applicationEventPublisher.publishEvent(new PayEvent(this, orderId));
        }
    }
}

这里支付成功后,会发布事件。这里的需求是支付后需要短信通知用户,通知订单修改状态,通知仓库准备发货。我们分别创建相关类来接收发布的事件。

OrderService

/**
 * Description:订单服务
 *
 * @author Lvshen
 * @version 1.0
 * @date: 2020-8-28 14:10
 * @since JDK 1.8
 */
@Component
public class OrderService {

    @EventListener
    public void updateOrderStatus(PayEvent payEvent) {
        String orderId = payEvent.getOrderId();
        //修改订单状态
        System.out.println(String.format("支付成功,修改订单【%s】状态为已支付!!!",orderId));
    }
}

当订单服务监听到支付服务发过来的数据,开始修改数据。

SmsService

/**
 * Description:短信服务
 *
 * @author Lvshen
 * @version 1.0
 * @date: 2020-8-28 14:16
 * @since JDK 1.8
 */
@Component
public class SmsService {

    @EventListener
    public void sendMessage(PayEvent payEvent) {
        String orderId = payEvent.getOrderId();
        //短信功能
        System.out.println(String.format("支付成功,发送【%s】短信",orderId));
    }
}

短信服务监听支付事件,当支付成功,监听到事件,并将支付成功的消息发送给用户。

WarehouseService

/**
 * Description:仓库服务
 *
 * @author Lvshen
 * @version 1.0
 * @date: 2020-8-28 14:21
 * @since JDK 1.8
 */
@Component
public class WarehouseService {
    @EventListener
    public void sendProduct(PayEvent payEvent) {
        String orderId = payEvent.getOrderId();
        //发货功能
        System.out.println(String.format("支付成功,准备发货,订单【%s】",orderId));
    }
}

同样,当仓库监听到支付成功的事件,开始准备发货。

测试事件发布机制【即当支付成功时】

显示结果

 

当需要添加微信通知时,只需要写一个微信服务类,在监听支付成功的事件了。同理,QQ通知和优惠券下发,积分下发等功能采用同样的方法。

这样我们不用去改动支付成功这个方法了,毕竟修改核心方法还是很危险的。

这里我抛出2个问题:

1.如果事件发布后,事务还没提交,另一边监听到了,可能会造成数据不准确问题,还有空指针异常问题。

2.如果发布事件一方有操作数据库,监听事件一方也有操作数据库。当监听事件一方操作数据库时抛出异常,发布事件一方需要回滚么。

不知这两个问题你的解决方案是什么呢?

往期推荐

扫码二维码,获取更多精彩。或微信搜Lvshen_9,可后台回复获取资料

1.回复"java" 获取java电子书;

2.回复"python"获取python电子书;

3.回复"算法"获取算法电子书;

4.回复"大数据"获取大数据电子书;

5.回复"spring"获取SpringBoot的学习视频。

6.回复"面试"获取一线大厂面试资料

7.回复"进阶之路"获取Java进阶之路的思维导图

8.回复"手册"获取阿里巴巴Java开发手册(嵩山终极版)

9.回复"总结"获取Java后端面试经验总结PDF版

10.回复"Redis"获取Redis命令手册,和Redis专项面试习题(PDF)

11.回复"并发导图"获取Java并发编程思维导图(xmind终极版)

另:点击【我的福利】有更多惊喜哦。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值