spring事件详解
一、场景
假设现在某电商平台天狗有这么个需求,在用户下完单之后,发送短信给用户。
public void order(){ // 下单成功 System.out.println("下单成功..."); // 发送短信 sendSms(); }
目前来看没什么问题,假设一个月后,天狗平台业务有调整,需要下单完通知仓储物流系统,准备发送货物,代码如下:
public void order(){ // 下单成功 System.out.println("下单成功..."); // 发送短信 sendSms(); // 通知车队发货 notifyCar(); }
三个月之后,自营效果不明显,砍掉了自营仓库,那代码如下:
public void order(){ // 下单成功 System.out.println("下单成功..."); // 发送短信 sendSms(); // 车队没了,注释掉这行代码 // notifyCar(); }
经过半年的发展,天狗平台决定还是要走自营的路线,代码如下:
public void order(){ // 下单成功 System.out.println("下单成功..."); // 发送短信 sendSms(); // 车队买回来了 notifyCar() }
二、问题分析
平时业务变化非常的快,这是需要从整个公司角度来看,那对于技术层面,如何应对这样的场景呢?以增量的方式应对变化的需求,以此引出spring的监听机制。
注:其实mq消息中间件也可以解决这样的问题,不过消息中间件解决的是系统与系统之间的问题,spring监听机制则可以在某应用内使用。
三、使用spring监听机制
新建springboot项目,这个就不细说了。
A、创建事件类
public class OrderSuccessEvent extends ApplicationEvent { /** * 创建订单完成事件类。事件类需继承ApplicationEvent类 */ public OrderSuccessEvent(Object source) { super(source); } }
B、订单方法
/** * 订单服务 */ @Service public class OrderService { @Autowired private ApplicationContext applicationContext; public void order() { // 下单成功 System.out.println("下单成功..."); // 发布通知 applicationContext.publishEvent(new OrderSuccessEvent(this)); System.out.println("main线程结束..."); } }
C、短信服务
/** * 短信服务,监听OrderSuccessEvent。需实现ApplicationListener接口。 */ @Service public class SmsService implements ApplicationListener<OrderSuccessEvent> { @Override public void onApplicationEvent(OrderSuccessEvent event) { this.sendSms(); } /** * 发送短信 */ public void sendSms() System.out.println("发送短信..."); } }
测试:
@RunWith(SpringRunner.class) @SpringBootTest public class Test { @Autowired private OrderService orderService; @Test public void testSpringEvent() { orderService.order(); } }
输出:
下单成功...
发送短信...
main线程结束...
那这么实现有什么好处呢?
比如后续又要新建个发货服务,如下:
/** * 物流服务 */ @Service public class CarService implements ApplicationListener<OrderSuccessEvent> { @Override public void onApplicationEvent(OrderSuccessEvent event) { this.dispatch(); } public void dispatch() { System.out.println("发车咯..."); } }
当然也可以使用注解方式,如下:
/** * 物流服务 */ @Service public class CarService { /** * 发送短信 @EventListener指定监听的事件 */ @EventListener(OrderSuccessEvent.class) public void dispatch() { System.out.println("发车咯..."); } }
发现了什么问题没有,上面这种是同步监听的方式,下完单之后,需要等发送短信结束、发货结束,这影响了下单的时间。
因此需要使用异步监听的方式。
四、异步监听
当SimpleApplicationEventMulticaster中的Executor不为null,就会执行异步通知。
@Configuration public class AsyncEventConfig { @Bean(name = "applicationEventMulticaster") public ApplicationEventMulticaster simpleApplicationEventMulticaster() { SimpleApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster(); eventMulticaster.setTaskExecutor(new SimpleAsyncTaskExecutor()); return eventMulticaster; } }
五、@TransactionalEventListener
假设现在有这么个场景,用户注册后发送激活码,代码如下:
void saveUser(User u) { //保存用户信息 userDao.save(u); //触发保存用户事件 applicationContext.publishEvent(new SaveUserEvent(u.getId())); } @EventListener void onSaveUserEvent(SaveUserEvent event) { //获取事件中的信息(用户id) Integer id = event.getEventData(); //查询数据库,获取用户(此时如果用户还未插入数据库,则返回空) User u = userDao.getUserById(id); //这里可能报空指针异常! String phone = u.getPhoneNumber(); MessageUtils.sendMessage(phone); }
为了保障监听事件处理类一定能获取到数据,则使用@TransactionalEventListener,代码如下:
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) void onSaveUserEvent(SaveUserEvent event) { Integer id = event.getEventData(); User u = userDao.getUserById(id); String phone = u.getPhoneNumber(); MessageUtils.sendMessage(phone); }
解释下TransactionPhase
public enum TransactionPhase { // 指定目标方法在事务commit之前执行 BEFORE_COMMIT, // 指定目标方法在事务commit之后执行 AFTER_COMMIT, // 指定目标方法在事务rollback之后执行 AFTER_ROLLBACK, // 指定目标方法在事务完成时执行,这里的完成是指无论事务是成功提交还是事务回滚了 AFTER_COMPLETION }
如果发布事件的地方没有手动加事务,那么需要额外加个配置:
/** * fallbackExecution默认是false,代表发生事件地方没有事务时是否继续执行 */ @TransactionalEventListener(fallbackExecution = true)
posted on 2021-04-30 12:49 阿里-马云的学习笔记 阅读(682) 评论(0) 编辑 收藏 举报