springboot的@EventListener监听事件

  • Post author:
  • Post category:其他



背景


在开发工作中,会遇到一种场景,做完某一件事情以后,需要广播一些消息或者通知,告诉其他的模块进行一些事件处理,一般来说,可以一个一个发送请求去通知,但是有一种更好的方式,那就是事件监听,事件监听也是设计模式中 发布-订阅模式、观察者模式的一种实现。

观察者模式:简单的来讲就是你在做事情的时候身边有人在盯着你,当你做的某一件事情是旁边观察的人感兴趣的事情的时候,他会根据这个事情做一些其他的事,但是盯着你看的人必须要到你这里来登记,否则你无法通知到他(或者说他没有资格来盯着你做事情)。

对于 Spring 容器的一些事件,可以监听并且触发相应的方法。通常的方法有 2 种,ApplicationListener 接口和**@EventListener** 注解。


简介


要想顺利的创建监听器,并起作用,这个过程中需要这样几个角色:

1、事件(event)可以封装和传递监听器中要处理的参数,如对象或字符串,并作为监听器中监听的目标。

2、监听器(listener)具体根据事件发生的业务处理模块,这里可以接收处理事件中封装的对象或字符串。

3、事件发布者(publisher)事件发生的触发者。




:那么问题来了,这个与消息中间件相比有什么区别呐?

可以把它看成spring自己提供的消息队列,优点就是不需要外部中间件依赖。



一、 spring默认事件监听

package com.example.anotationaop.lisener;

import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

/**
 * 实现 ApplicationListener<ApplicationEvent> 接口,重写onApplicationEvent方法
 */
@Component
public class MyApplicationListener implements ApplicationListener<ApplicationEvent>  {
    @Override
    public void onApplicationEvent(ApplicationEvent applicationEvent) {
        System.out.println("pring默认事件触发:"+applicationEvent.getClass().getName());
    }
}


二、 自定义事件,非注解方式

首先定义一个事件(MyTestEvent),需要继承Spring的

ApplicationEvent

package com.example.anotationaop.event;

import org.springframework.context.ApplicationEvent;

public class MyTestEvent extends ApplicationEvent {
    private static final long serialVersionUID = 1L;

    private String msg ;

    public MyTestEvent(Object source,String msg) {
        super(source);
        this.msg = msg;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }
}

再定义一个监听器,自己定义的监听器需要实现ApplicationListener,同时泛型参数要加上自己要监听的事件Class名,在重写的方法onApplicationEvent中,添加自己的业务处理:

package com.example.anotationaop.lisener;

import com.example.anotationaop.event.MyTestEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

@Component
public class MyNoAnnotationListener implements ApplicationListener<MyTestEvent> {

    @Override
    public void onApplicationEvent(MyTestEvent event) {
        System.out.println("非注解监听器:" + event.getMsg());
    }

}

事件触发者,每次想让监听器收到事件通知的时候,就可以调用一下事件发布的操作。首先在类里自动注入了

ApplicationEventPublisher

,这个也就是我们的

ApplicationContext

,它实现了这个接口。

package com.example.anotationaop.lisener;

import com.example.anotationaop.event.MyTestEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;

@Component
public class MyTestEventPubLisher {
    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;
    /**
     *  事件发布方法
     */
    public void pushListener(String msg) {
        applicationEventPublisher.publishEvent(new MyTestEvent(this, msg));
    }

}

使用:

package com.example.anotationaop.controller;

import com.example.anotationaop.lisener.MyTestEventPubLisher;
import com.example.anotationaop.service.TestService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {

    @Autowired
    private TestService testService;

    @Autowired
    private MyTestEventPubLisher publisher;

    @RequestMapping("/test")
    public String test(){
        // 调用监听方法
        publisher.pushListener("非注解自定义监听器");
        return testService.test("测试");
    }
}

打印日志:

pring默认事件触发:com.example.anotationaop.event.MyTestEvent
非注解监听器:非注解自定义监听器


三、 使用注解自定义事件以及监听

1、 默认事件

在任意方法上标注@EventListener 注解,指定 classes,即需要处理的事件类型,一般就是 ApplicationEven 及其子类,可以设置多项。

package com.example.anotationaop.config;

import org.springframework.context.ApplicationEvent;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.EventListener;

@Configuration
public class Config {
    @EventListener(classes = {ApplicationEvent.class})
    public void listen(ApplicationEvent event) {
       System.out.println("注解默认事件触发:" + event.getClass().getName());
    }
}

打印日志:

注解默认事件触发:org.springframework.boot.web.servlet.context.ServletWebServerInitializedEvent
注解默认事件触发:org.springframework.context.event.ContextRefreshedEvent
2022-11-22 20:43:29.531  INFO 17228 --- [           main] c.e.a.AnotationAopApplication            : Started AnotationAopApplication in 0.778 seconds (JVM running for 1.099)
注解默认事件触发:org.springframework.boot.context.event.ApplicationStartedEvent
注解默认事件触发:org.springframework.boot.availability.AvailabilityChangeEvent
注解默认事件触发:org.springframework.boot.context.event.ApplicationReadyEvent
注解默认事件触发:org.springframework.boot.availability.AvailabilityChangeEvent

2. 注解自定义事件以及监听

使用注解的好处是不用每次都去实现ApplicationListener,可以在一个class中定义多个方法,用@EventListener来做方法级别的注解。

和上面类似,事件以及事件发布不需要改变,只要这样定义监听器即可。

package com.example.anotationaop.lisener;

import com.example.anotationaop.event.MyTestEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

@Component
public class MyAnnotationListener {

    @EventListener
    public void listener1(MyTestEvent event) {
        System.out.println("注解监听器1:" + event.getMsg());
    }
}

打印日志:

注解默认事件触发:com.example.anotationaop.event.MyTestEvent
注解监听器1:非注解自定义监听器
非注解监听器:非注解自定义监听器

我们可以发现,注解形式的监听器的执行走在了非注解的前面。这里看原理的同学直接去看原文吧

(1条消息) SpringBoot中@EventListener注解的使用_njitzyd的博客-CSDN博客_eventlistener注解无效


总结


上面介绍了@EventListener的原理,其实上面方法里还有一个

@TransactionalEventListener

注解,其实原理是一模一样的,只是这个监听者可以选择在事务完成后才会被执行,事务执行失败就不会被执行。

这两个注解的逻辑是一模一样的,并且@TransactionalEventListener本身就被标记有@EventListener,

只是最后生成监听器时所用的工厂不一样而已。