spring event-listener模型
Posted 骆宏
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring event-listener模型相关的知识,希望对你有一定的参考价值。
在讨论spring event-listener之前,我们先来看一个设计模式-订阅者设计模式。这个模式在编程世界很常见,比如tomcat的event-listener,spring event-listener等都是实际案例。
下面给一个简单版本的demo
package com.jt.ec.salesorder.domain;
import java.util.ArrayList;
import java.util.List;
public class EventListenerDemo
/**
* 定义一个报刊
*/
public static abstract class News
public News()
/**
* 订阅的内容
*/
public abstract void readNew(String content);
/**
* 报刊管理中心
*/
public static class NewsManager
private List<People> buyers;
public NewsManager()
People p = new People("luohong");
People p1 = new People("luohong1");
People p2 = new People("luohong2");
addBuyer(p);
addBuyer(p1);
addBuyer(p2);
public List<People> getBuyers()
return buyers;
/**
* 添加多个订阅者
*/
public void addBuyers(List<People> buyers)
this.buyers = buyers;
/**
* 添加一个订阅者
*/
public void addBuyer(People buyer)
if(this.buyers == null)
this.buyers = new ArrayList<>();
this.buyers.add(buyer);
/**
* 通知订阅的读者
*/
public void sendToBuyers()
for(People buyer: buyers)
buyer.readNew("<哈哈,欢迎您的订阅,我是骆宏...>");
/**
* 读者
*/
public static class People extends News
private String name;
public People(String name)
this.name = name;
public String getName()
return name;
public void setName(String name)
this.name = name;
@Override
public void readNew(String content)
System.out.println(name + "阅读了" + content);
public static void main(String[] args)
NewsManager manager = new NewsManager();
manager.sendToBuyers();
/*
* 输出内容
* luohong阅读了<哈哈,欢迎您的订阅,我是骆宏...>
* luohong1阅读了<哈哈,欢迎您的订阅,我是骆宏...>
* luohong2阅读了<哈哈,欢迎您的订阅,我是骆宏...>
*/
这里面,News代表一个主题,People是该主题的具体关注着,NewsManager起到一个管理的作用,在代码中,我们可以发现,People只需要注册感兴趣的News,具体什么时候阅读,有NewsManager来完成调用。这种模型的代码,能够很好的实现解耦。
具体到spring中,我们该怎么使用event-listener呢?由于spring已经将抽象出了event,listener了,所以我们只需要按照spring的约定,然后将listener注册为一个bean,即可在抛出事件时,listener完成调用。这次的主题,不在于spring的实现细节,而是讨论,什么情境下该使用event-listener。我们看一个实际的代码案列,如下所示
if(CollectionUtils.isNotEmpty(financeBillPos))
//3循环所有的财务单据,把完成和审核通过的财务单据的金额进行统计
for(FinanceBillPo financeBillPo : financeBillPos)
if(financeBillPo.getStatus().equals(FinanceBillStatus.done.name()))
if(financeBillPo.getType().equals(FinanceBillType.receipt.name()))
//3.1收款单,退款的金额=+已收款的金额
totalAmt=totalAmt.add(financeBillPo.getTotal());
cpAccount=financeBillPo.getPayAccount();
else if(financeBillPo.getType().equals(FinanceBillType.pay.name()) || financeBillPo.getType().equals(FinanceBillType.refund.name()))
//3.2付款单/退款单,退款的金额=-已付款/已退款的金额
totalAmt=totalAmt.subtract(financeBillPo.getTotal());
else
//绿色通道主订单的生成退款单
totalAmt = soEntityPo.getPayedAmount();
//4金额大于零则生成退款单
if(totalAmt.compareTo(BigDecimal.ZERO)==1)
String summary="订单["+soEntityPo.getSoNo()+"]"+soEntityPo.getCashTypeEnum().desc()+";退款金额:";
FinanceBillResult financeBillResult =financeBillService.addRefundBill(FinanceBillEntityType.salesorder.name(), soEntityPo.getSoNo(), totalAmt, summary+totalAmt.toString(), soEntityPo.getPayType(), cpAccount);
SoBillEvent soBillEvent = new SoBillEvent(financeBillResult.getFinanceBillPo(),soEntityPo.getId(),true);
SpringHelper.publishEvent(soBillEvent);
我们忽略其中的业务代码,关注最后面的一部分,这里面抛出了一个SoBillEvent,然后这个Event的业务,该开发者也已经备注好了,就是生成一个退款单。但是从这个代码来看,我们发现压根看不懂,如果删除4金额大于零则生成退款单的备注,那么我们就更加难懂这块代码了。
综合上面所述,我们可以明显的看到,event-listener的模型,硬生生将一个业务,然后拆分为了多个业务,并且多个业务是未知的,你不知道会有多少个listener来监听该event,更加不知道每个listener的业务意义了。假设你是一个维护代码的开发者,看到event时,你不得不去查看该event的所有listener,然后逐一找出来,然后逐个观察里面的业务。event-listener模型将一个线性的代码块,拆分为了一个tree结构的代码块,所以这种是属于典型的误用event-listener编程模型的例子。
那我们以更加抽象的方式来观察下event-listener模型,见下面的伪代码
try
saveOrderItem();
saveShipingInfo();
saveSoLog();
placeOrder();
operOrderProfix(); //创建业绩
try
saveOrderItem();
saveShipingInfo();
saveSoLog();
placeOrder();
var event = createOrderEvent(); //跑出一个事件,在listener中创建业绩
publishEvent(event);
我们可以对比两种代码的差异点,第一个方案,直接在逻辑代码中,调用创建业绩的代码,第二个方案,却新建了两个类,然后将线性代码拆分为了tree模型。
那么什么时候合适使用event-listener呢?根据我个人的实践,推荐在如下几种模型中,可以考虑使用event-listener
1.执行无关业务代码,比如:记录日志等
2.需要异步执行的代码,比如:下载文件
3.跨系统调用,比如集成第三方的api等
总结
使用event-listener模型时,需要非常的谨慎,避免误用。一般我个人在使用时,会做如下几点的思考
1.考核业务的连续性
2.考核添加event-listener后,维护代码的同事能否一眼看出event,listener的业务意义
3.如果是需要使用异步编程模型,可以考虑使用
4.如果使用了event-listener,那么遵循:one event one listener,并且不允许在listener中继续抛出其他类型event
以上是关于spring event-listener模型的主要内容,如果未能解决你的问题,请参考以下文章