SpringBoot自定义事件监听

1.前提知识点

(1)事件监听机制的好处是什么?

事件监听机制实际就是主题-订阅模式(观察者模式)的实现,能够降低代码耦合、简单的实现时间通知处理机制(有点像任务状态机)。

(2)SpringBoot事件监听核心接口和流程


在spring中,事件监听机制主要涉及4个实现类。

ApplicationEvent:事件,每个实现类表示一类事件,可携带数据。抽象类。
ApplicationListener:事件监听器,用于接收事件处理时间。接口。
ApplicationEventMulticaster:事件管理者,可以注册(添加)/移除/发布事件。用于事件监听器的注册和事件的广播。接口。
ApplicationEventPublisher:事件发布者,委托事件管理者ApplicationEventMulticaster完成事件发布。类。

2.SpringBoot自定义事件、监听器总结

为什么要先理解自定义事件监听实现?

只有理解自定义事件监听实现,体会到其中的设计思想,在看SpringBoot源码时才有助于理解SpringBoot内部事件监听器机制。

自定义事件、监听器流程:

流程总结:首先,自定义事件继承ApplicationEvent抽象类,可以定义一些方法。其次,自定义监听器实现ApplicationListener接口的onApplicationEvent方法/使用@EventListener注解。最后,调用context或注入ApplicationEventPublisher对象发布事件。

知识来源

SpringBoot事件监听有四种方式,前三者都是实现ApplicationListener接口的onApplicationEvent方法,最后一个是注解实现自己的方法:

  • 向ApplicationContext中添加监听器
  • 将监听器作为组件加载到spring容器
  • 在application.properties中配置监听器
  • 通过@EventListener注解实现事件监听

2.1 自定义事件

public class MyEvent extends ApplicationEvent {
    public MyEvent(Object source) {
        super(source);
    }
}

2.2 自定义监听器

(1)向ApplicationContext中添加监听器

示例代码。下述代码前者实现了ApplicationListener接口onApplicationEvent方法的自定义监听器,后者添加监听器到context中。

//实现ApplicationListener接口的onApplicationEvent方法
public class MyEventListener1 implements ApplicationListener<MyEvent> {
    @Override
    public void onApplicationEvent(MyEvent myEvent) {
        Object msg = myEvent.getSource();
        System.out.println("自定义事件监听器(MyEventListener1)收到发布的消息: " + msg);
    }
}

//启动类
@SpringBootApplication
public class LearningApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(LearningApplication.class, args);
        context.addApplicationListener(new MyEventListener1());//通过context添加监听器
    }
}

(2)将监听器作为组件加载到spring容器

示例代码。将实现ApplicationListener接口的onApplicationEvent方法的自定义类,作为组件(@Componen)添加到springboot容器中。

@Component
public class MyEventListener2 implements ApplicationListener<MyEvent> {
    @Override
    public void onApplicationEvent(MyEvent myEvent) {
        Object msg = myEvent.getSource();
        System.out.println("自定义事件监听器(MyEventListener2)收到发布的消息: " + msg);
    }
}

(3)在application.properties中配置监听器

示例代码。自定义实现ApplicationListener接口的onApplicationEvent方法的自定义类,在配置文件中配置自定义监听器。

public class MyEventListener3 implements ApplicationListener<MyEvent> {
    @Override
    public void onApplicationEvent(MyEvent myEvent) {
        Object msg = myEvent.getSource();
        System.out.println("自定义事件监听器(MyEventListener3)收到发布的消息: " + msg);
    }
}


//application.properties文件中配置
context:
  listener:
    classes: com.xqnode.learning.common.event.MyEventListener3

(4)通过@EventListener注解实现事件监听

@Component
public class MyEventListener4 {
    @EventListener//在方法上添加注解,表示该方法是监听器响应的方法
    public void listener(MyEvent myEvent) {
        Object msg = myEvent.getSource();
        System.out.println("自定义事件监听器(MyEventListener4)收到发布的消息: " + msg);
    }
}

2.3 发布自定义事件

只有发布了事件,监听器采集监听到事件,进而触发监听器方法中对应的操作。

(1)使用context发布事件

@SpringBootApplication
public class LearningApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(LearningApplication.class, args);
        context.publishEvent(new MyEvent("测试事件"));
    }
}

(2)注入ApplicationEventPublisher对象发布事件

@Autowired
    private ApplicationEventPublisher applicationEventPublisher;//注入spring事件发布者,用于发布事件 
applicationEventPublisher.publishEvent(new MyEvent("测试事件"));//发布事件。

3.SpringBoot自定义事件、监听器具体实现示例

整体流程:

自定义事件   ---->  利用ApplicationEventPublisher发布事件    ---->     使用注解@EventListener自定监听器   ---->      监听器内作相应处理

注:代码示例来源

(1)定义简单实体类

@Component
public class Order {
    private String orderNo;
    private String phone;

    public String getOrderNo() {
        return orderNo;
    }

    public void setOrderNo(String orderNo) {
        this.orderNo = orderNo;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }
}

(2)自定义事件 

@Component
public class AfterCreateOrderEvent extends ApplicationEvent {//继承ApplicationEvent
    public AfterCreateOrderEvent(Order order) {
        super(order);
    }
    public Order getOrder(){
        return (Order) getSource();
    }
}

(3)事件发布和自定义监听器

@Service
public class OrderService {
    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;//注入spring事件发布者,用于发布事件
    /**
     * 简单的创建订单方法
     */
    public void createOrder(Order order) {
        System.out.println("创建订单 order:" + order.getOrderNo() + " 结束");
        // TODO 1. 调用事件发布者发布事件,解耦
        applicationEventPublisher.publishEvent(new AfterCreateOrderEvent(order));//发布事件。AfterCreateOrderEvent继承自ApplicationListener
        System.out.println("createOrder方法 结束");
    }
    // TODO 2. 事件监听处理,加入@EventListener注解后,该方法可以看作事件监听者。参数就是自定义的事件。
    @EventListener
    public void afterCreateOrder(AfterCreateOrderEvent afterCreateOrderEvent) throws InterruptedException {
        Order order = afterCreateOrderEvent.getOrder();
        Thread.sleep(2000);
        System.out.println("调用短信通知服务:" + order.getPhone());
        System.out.println("调用积分服务增加贷款积分:" + order.getOrderNo());
    }
}

(4)程序启动和结果

@RunWith(SpringRunner.class)
@SpringBootTest//引入了SpringBootTest测试用的jar包依赖
public class ApplicationTest {
    @Autowired
    private OrderService orderService;

    @Test
    public void eventTest() {
        Order order = new Order();
        order.setOrderNo("N123124124124");
        order.setPhone("18782202534");
        orderService.createOrder(order);
    }
}
创建订单 order:N123124124124 结束
调用短信通知服务:18782202534
调用积分服务增加贷款积分:N123124124124
createOrder方法 结束

4.SpringBoot自定义监听器同步、异步分析

总结:通过applicationEventPublisher和@EventListener发布、接收事件,其实是同步的。这意味着,先后发布事件A、B,对应的监听器执行完毕A通知的事件之后,才会执行B通知的事件。

4.1 同步监听器代码示例

验证逻辑:主线程同时发布2个事件,查看主线程是否会被阻塞。

/**
    事件类
*/

public class MyEvent extends ApplicationEvent {
    String threadName = "";
    public MyEvent(Object source,String threadName){
        super(source);
        this.threadName = threadName;
    }

    public String getThreadName() {
        return threadName;
    }

    public void setThreadName(String threadName) {
        this.threadName = threadName;
    }
}



/**
 事件监听器,内部睡眠20s
*/
@Component
public class EventListenerHandler {
//    @Async
    @EventListener
    public void listener(MyEvent myEvent){
        System.out.println("监听器开始执行,当前线程名字:"+myEvent.getThreadName());
        try {
            Thread.sleep(20000);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}



/**
发布事件
*/
@Component
public class TestListenerRunner implements ApplicationRunner {
    @Autowired
    ApplicationEventPublisher applicationEventPublisher;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("runner开始...准备睡眠2s");
        applicationEventPublisher.publishEvent(new MyEvent(new Object(),"thread1"));
        applicationEventPublisher.publishEvent(new MyEvent(new Object(),"thread2"));
        System.out.println("主线程执行结束...");

    }
}

输出:

runner开始...准备睡眠2s
监听器开始执行,当前线程名字:thread1

可以看出,监听器内部代码逻辑被sleep阻塞,主线程等待监听器内部代码执行完毕才继续执行。

4.2 异步事件监听器代码示例

代码逻辑:在主应用类添加@EnableAsync注解表示线程池,同时在监听器方法处加入异步监听器。其他保持一致

@SpringBootApplication
@EnableAsync
public class SpringbootTestApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootTestApplication.class, args);
    }

}


@Component
public class EventListenerHandler {
    @Async//异步监听器
    @EventListener
    public void listener(MyEvent myEvent){
        System.out.println("监听器开始执行,当前线程名字:"+myEvent.getThreadName());
        try {
            Thread.sleep(20000);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

输出:

runner开始...准备睡眠2s
主线程执行结束...
监听器开始执行,当前线程名字:thread1
监听器开始执行,当前线程名字:thread2

结果分析:由结果可以看出,主线程已经执行结束,两次发布的事件对应的监听事件都已经开始执行。

注意:异步监听器执行的顺序,和发布的顺序并不一致。

同时发布多个事件后,异步监听器打印结果如下:

runner开始...准备睡眠2s
主线程执行结束...
监听器开始执行,当前线程名字:thread1
监听器开始执行,当前线程名字:thread5
监听器开始执行,当前线程名字:thread6
监听器开始执行,当前线程名字:thread2
监听器开始执行,当前线程名字:thread7
监听器开始执行,当前线程名字:thread8
监听器开始执行,当前线程名字:thread3
监听器开始执行,当前线程名字:thread4

  • 9
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值