Java中事件监听 Spring

2022-04-14 约 2101 字 阅读时长5 分钟

Java中事件监听

应用场景

案例

假设现在有这么一个业务场景:

用户在京西商城下单成功后,平台要发送短信通知用户下单成功

最直观的想法是直接在order()方法中添加发送短信的业务代码:

java
1public void order(){
2  // 下单成功
3  System.out.println("下单成功...");
4  // 发送短信
5  sendSms();
6}

乍一看没什么不妥,但是如果我们加上一根时间轴,那么代码就有问题了:

一个月后,京西搞了自建物流体系,用户下单成功后,需要通知物流系统发货

于是你又要打开OrderService修改order()方法:

java
1public void order(){
2  // 下单成功
3  System.out.println("下单成功...");
4  // 发送短信
5  sendSms();
6  // 通知车队发货 
7  notifyCar();
8}

又过了一个月,东哥被抓了,股价暴跌,决定卖掉自己的车队,所以下单后就不用通知车队了

重新修改OrderService:

java
1public void order(){
2  // 下单成功
3  System.out.println("下单成功...");
4  // 发送短信
5  sendSms();
6  // 车队没了,注释掉这行代码 
7  // notifyCar();
8}

又过了一个月,东哥明尼苏达州荣耀归来:回来做我的兄弟一起开车吧。

好的,东哥。

java
1public void order(){
2  // 下单成功
3  System.out.println("下单成功...");
4  // 发送短信
5  sendSms();
6  // 车队买回来了
7  notifyCar()
8}

车队回来了,你却受不了这大起大落异常刺激的生活,决定离职

以增量的方式应对变化的需求

img

Spring事件监听

业务代码

java
 1/**
 2 * 订单服务
 3 */
 4@Service
 5public class OrderService {
 6
 7    @Autowired
 8    private ApplicationContext applicationContext;
 9
10    public void order() {
11        // 下单成功
12        System.out.println("下单成功...");
13        // 发布通知
14        applicationContext.publishEvent(new OrderSuccessEvent(this));
15        System.out.println("main线程结束...");
16    }
17}

自定义事件

OrderSuccessEvent(继承ApplicationEvent,自定义事件)

java
1public class OrderSuccessEvent extends ApplicationEvent {
2	//这里还可以自定义参数,并通过构造方法赋值
3    public OrderSuccessEvent(Object source) {
4        super(source);
5    }
6}

监听事件

可以指定监听者监听的事件类型,那么就会对未声明的事件不进行监听

SmsService(实现ApplicationListener,监听OrderSuccessEvent)

java
 1/**
 2 * 短信服务,监听OrderSuccessEvent
 3 */
 4@Service
 5public class SmsService implements ApplicationListener<OrderSuccessEvent> {
 6
 7    @Override
 8    public void onApplicationEvent(OrderSuccessEvent event) {
 9        this.sendSms();
10    }
11
12    /**
13     * 发送短信
14     */
15    public void sendSms() 
16        System.out.println("发送短信...");
17    }
18}

也可以通过注解的方式监听事件

java
1@EventListener(classes = OrderSuccessEvent.class)
2public void onApplicationEvent(OrderSuccessEvent event) {
3    System.out.println("发送短信...");
4}

Test

java
 1@RunWith(SpringRunner.class)
 2@SpringBootTest
 3public class Test {
 4
 5    @Autowired
 6    private OrderService orderService;
 7
 8    @Test
 9    public void testSpringEvent() {
10        orderService.order();
11    }
12}
13
14//输出
15//	下单成功...
16//	发送短信...
17//	main线程结束...

异步调用

某些时候,为了推送第三方信息的执行不影响业务代码,可以通过在监听事件的方法上添加注解@Async注解,开启异步执行(启动类需要配置注解:@EnableAsync)

事务监听

对于业务代码需要提交事务后,或者说等待主线程执行完毕后再推送第三方信息,则可以使用事务监听

和普通事件监听类似,先定义事件,其次再定义事件监听器,事件监听器的代码如下

java
1// phase 指定事务什么情况下执行该监听器
2// classes 指定事件
3@Async
4@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT,classes = MyApplicationEvent.class)
5public void onApplicationEventLister(MyApplicationEvent event) {
6    System.out.println("时间监听器B 收到事件:" + event);
7}

示例

  1. 创建事件抽象类,以及其实现

    java
     1//事件抽象类
     2public abstract class TestEvent extends ApplicationEvent {
     3    private static final long serialVersionUID = -7558477450921553407L;
     4    private String listenerType;
     5    private String msgTwo;
     6
     7    protected TestEvent(Object source) {
     8        super(source);
     9    }
    10
    11    protected TestEvent(Object source, Clock clock) {
    12        super(source, clock);
    13    }
    14
    15    public String getListenerType() {return listenerType;}
    16
    17    public void setListenerType(String listenerType) {this.listenerType = listenerType;}
    18
    19    public String getMsgTwo() {return msgTwo;}
    20
    21    public void setMsgTwo(String msgTwo) {this.msgTwo = msgTwo;}
    22}
    23
    24
    25//事件实现类,事件T
    26public class EventT extends TestEvent{
    27    private static final long serialVersionUID = -3817485810073854944L;
    28    private String mineMsg;
    29    public EventT(String listenerType,Object source, String mineMsg) {
    30        super(source);
    31        super.setListenerType(listenerType);
    32        this.mineMsg=mineMsg;
    33    }
    34
    35    public String getMineMsg() {return mineMsg;}
    36
    37    public void setMineMsg(String mineMsg) {this.mineMsg = mineMsg;}
    38}
  2. 创建事件监听抽象类,以及其实现

    java
     1//事件监听器抽象类,也可通过注解,不实现ApplicationListener接口
     2public abstract class TestEventListener implements ApplicationListener<TestEvent> {
     3    @Override
     4    public void onApplicationEvent(TestEvent event) {
     5        if (event.getSource() instanceof String) {
     6            handleBusinessData(event);
     7        } else {
     8            throw new IllegalArgumentException("事件类型代码错误");
     9        }
    10    }
    11    protected abstract void handleBusinessData(TestEvent event);
    12
    13    //通过注解实现事件监听,而不实现ApplicationListener接口
    14/*  @Async
    15    @EventListener(TestEvent.class)
    16    public void onApplicationEvent(TestEvent event) {
    17        if (event.getSource() instanceof String) {
    18            handleBusinessData(event);
    19        } else {
    20            throw new IllegalArgumentException("事件类型代码错误");
    21        }
    22    }*/
    23}
    24
    25//事件监听器A实现类,可以根据source与ListenerType对多个不同事件处理不同处理逻辑
    26@Component
    27public class EventListenerA extends TestEventListener{
    28    @Override
    29    protected void handleBusinessData(TestEvent event) {
    30        if (!"listenerA".equals(event.getListenerType())) {
    31            return;
    32        }
    33        String code= (String) event.getSource();
    34        switch (code){
    35            case "EventT":
    36                System.out.println("EventT");
    37                break;
    38            default:
    39                throw new RuntimeException("未实现的事件方法");
    40        }
    41    }
    42}
  3. 事件发布

    java
     1//事件发布工具类,实现ApplicationContextAware接口,会自动注入ApplicationContext
     2@Component
     3public class PubEventUtil implements ApplicationContextAware {
     4    private static ApplicationContext applicationContext;
     5    @Override
     6    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
     7        PubEventUtil.applicationContext=applicationContext;
     8    }
     9    public static void pubEvent(ApplicationEvent event){
    10        applicationContext.publishEvent(event);
    11    }
    12}
    13
    14//发布事件
    15TestEvent eventT = new EventT("listenerA","EventT", "msg");
    16PubEventUtil.publishEvent(eventT);

消息队列发布订阅模式

使用滚轮缩放
按住拖动