Spring事件发布机制-最佳实践

185 阅读4分钟

小年之前分享了很多 MQ的文章,相信大家对消息中间件也很熟悉了。今天这篇呢,不聊别的,就想分享一个MQ简单且使用的干货。

先提个问题:不知道大家有没有这样的经历,MQ消费端的代码堆着各种各样的业务逻辑。随着业务需求增加,代码的逻辑也随着增加。有时候改动一点逻辑,又怕影响到其他业务,可谓是牵一发而动全身。

举个栗子吧:用户支付下单的业务场景。

用户发起支付,业务系统请求订单系统创建交易订单。一般来说,订单的交易处理会比较复杂和耗时,所以订单的支付结果通常都是异步返回的。要不就是异步回调,要不就是消息通知。

假设这里采用消息通知的方式,业务系统需要订阅相应的Topic进行处理。大致的代码实现如下(这里使用 RocketMQ 为例子来实现)

@RocketMQMessageListener(topic = "xxx", consumerGroup = "xxx")
public class OrderConsumer implements RocketMQListener<Order> {
    @Override
    public void onMessage(Order order) {
        // 处理订单结果
        handleOrder();
    }
}

但如果业务需求要对不同类型的订单做不同的处理,比如分为充值订单、话费订单、购物订单等,不同类型的订单可能会有定制化的要求。

按照这种需求,我们一般可能会这么实现:

@RocketMQMessageListener(topic = "xxx", consumerGroup = "xxx")
public class OrderConsumer implements RocketMQListener<Order> {
    @Override
    public void onMessage(Order order) {
        // 处理订单结果
        if (order.getType() == 'recharge') {
        
        }else if (order.getType() == 'shopping') {
          
        } 
       ...
    }
}

没错,对订单的类型做 if else 的判断。一般来说如果类型不多,业务逻辑不复杂,这种方式足以解决了。但如果后面还有其他类型的拓展实现,且各类型的业务逻辑错综复杂,那么这个 Consumer 就会越来越臃肿,甚至有可能成为了祖传代码。而且其他分支的修改,有可能会影响整个的的逻辑。那就真的是,谁接盘谁头痛。

聪明的同学可能马上就想到对策,我用工厂方法 + 策略模式不就可以解决了吗?

这当然没毛病,确实可以解决上面所述的问题。但你以为就这么简单吗?小年今天分享的是另外一种更优雅的方式。

Spring 事件发布监听

Spring 事件发布监听,本质上来说是观察者模式的应用。

  • ApplicationEvent:事件
  • ApplicationEventPublisher:事件发布者
  • ApplicationEventListenr:事件监听者

下面直接来看一下如何使用 Spring 事件发布解决上述的问题:

Consumer 中就不再有处理任何业务逻辑代码了,而是将消息包装成事件后,发布出去。

@RocketMQMessageListener(topic = "xxx", consumerGroup = "xxx")
public class OrderConsumer implements RocketMQListener<Order> {
​
    @Autowired
    private ApplicationContext applicationContext;
  
    @Override
    public void onMessage(Order order) {
        // 事件发布
        applicationContext.publishEvent(new OrderEvent(event));
    }
}
​
// 定义事件
public class OrderEvent extends ApplicationEvent {
  
    public OrderEvent(Order order) {
        super(order);
    }
  
    public OrderEvent getSource() {
        return (OrderEvent) super.getSource();
    }
}

对不同的订单类型创建事件监听类,需要实现接口 ApplicationListener

@Component
public class RechargeOrderListener implements ApplicationListener<OrderEvent> {
    @Override
    public void onApplicationEvent(OrderEvent event) {
        Order order = event.getSource();
        if (order.getType = "recharge") {
            // 充值订单处理
        }
    }
}
​
@Component
public class ShoppingOrderListener implements ApplicationListener<OrderEvent> {
    @Override
    public void onApplicationEvent(OrderEvent event) {
        Order order = event.getSource();
        if (order.getType = "shopping") {
            // 购物订单处理
        }
    }
}

PS:也可以通过 @EventListener 注解的方式实现事件的监听。

显而易见,使用 Spring 事件发布机制后,原本需要在一个 Consumer 下写一大堆逻辑的,现在拆分成各个独立且不想互相影响的模块。

当然,按照上面贴出来的代码,事件监听者(Listener)还是需要根据 order.getType() 判断是否需要执行,似乎还是有点重复和繁琐。对有代码洁癖的小年来说,这当然是不能容忍的,于是再来一个魔法吧,模板方法

public abstract class AbstractOrderListener implements ApplicationListener<OrderEvent> {
    @Override
    public void onApplicationEvent(OrderEvent event) {
        Order order = event.getSource();
        if (getType().contains(order.getTransType())) {
            onMessage(order);
        }
    }
​
    public abstract void onMessage(OrderEvent order);
​
    public abstract List<String> getType();
}
​
@Component
public class RechargeOrderListener extend AbstractOrderListener {
    @Override
    public void handleEvent(Order order) {
        // 充值订单处理        
    }
}

至此,整个优化流程就差不多结束了。从原先 Consumer 堆砌所有业务代码,到现在模块化的拆分。看完之后,就一个字,爽!

普通的改变,将改变普通

我是宅小年,一个在互联网低调前行的小青年

关注公众号「宅小年」,个人博客 📖 edisonz.cn,阅读更多分享文章