阿里-马云的学习笔记

导航

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编辑  收藏  举报