【基础系列】事件机制的两种消费姿势

文章目录
  1. I. 项目环境
  2. II. 事件机制
    1. 1. 事件对象
    2. 2. 接口方式消费
    3. 3. 注解方式消费
    4. 4. 发布事件
    5. 5. 测试
  3. II. 其他
    1. 0. 项目
    2. 1. 一灰灰Blog

借助Spring可以非常简单的实现事件监听机制,本文简单介绍下面向接口与注解监听的两种姿势

I. 项目环境

本项目借助SpringBoot 2.2.1.RELEASE + maven 3.5.3 + IDEA进行开发

为了后面的发布事件验证,起一个web服务

1
2
3
4
5
6
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>

II. 事件机制

1. 事件对象

在Spring中,所有的事件需要继承自ApplicationEvent,一个最基础的MsgEvent如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class MsgEvent extends ApplicationEvent {
private String msg;

/**
* Create a new {@code ApplicationEvent}.
*
* @param source the object on which the event initially occurred or with
* which the event is associated (never {@code null})
*/
public MsgEvent(Object source, String msg) {
super(source);
this.msg = msg;
}

@Override
public String toString() {
return "MsgEvent{" +
"msg='" + msg + '\'' +
'}';
}
}

2. 接口方式消费

消费事件有两种方式,接口的声明,主要是实现ApplicationListener接口;注意需要将listener声明为Spring的bean对象

1
2
3
4
5
6
7
@Service
public class MsgEventListener implements ApplicationListener<MsgEvent> {
@Override
public void onApplicationEvent(MsgEvent event) {
System.out.println("receive msg event: " + event);
}
}

3. 注解方式消费

实现接口需要新建实现类,更简单的方法是直接在消费方法上加一个注解@EventListener

1
2
3
4
@EventListener(MsgEvent.class)
public void consumer(MsgEvent msgEvent) {
System.out.println("receive msg by @anno: " + msgEvent);
}

这个注解,支持根据Event参数类型进行匹配,即上面的实例中,方法上直接加@EventListener不指定圆括号内部的也没关系

4. 发布事件

前面是消费事件,消费的前提是有事件产生,在Spring中,发布事件主要需要借助ApplicationContext来实现

1
2
3
4
5
6
7
8
9
10
11
12
13
@Service
public class MsgPublisher implements ApplicationContextAware {
private ApplicationContext applicationContext;

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}

public void publish(String msg) {
applicationContext.publishEvent(new MsgEvent(this, msg));
}
}

5. 测试

一个简单的测试demo

1
2
3
4
5
6
7
8
9
10
11
@RestController
public class IndexController {
@Autowired
private MsgPublisher msgPublisher;

@GetMapping(path = "pub")
public String publish(String msg) {
msgPublisher.publish(msg);
return "ok";
}
}

访问: curl http://localhost:8082/pub?msg=一灰灰blog

输出日志:

1
2
receive msg by @anno: MsgEvent{msg='一灰灰blog'}
receive msg event: MsgEvent{msg='一灰灰blog'}

上面这个测试两种消费方式都可以成功,但是,在实测的过程中发现一种case,注解消费方式不生效,测试姿势如下

1
2
3
4
5
6
7
8
9
10
11
@SpringBootApplication
public class Application {

public Application(MsgPublisher msgPublisher) {
msgPublisher.publish("一灰灰blog");
}
public static void main(String[] args) {
SpringApplication.run(Application.class);
}

}

直接在启动类的构造方法中发布事件,发现接口方式可以接收事件,但是注解方式不生效,why?

在stockoverstack上有个相似的问题 https://stackoverflow.com/questions/38487474/springboot-eventlistener-dont-receive-events,这里主要提了一个观点

  • 发布消息比事件消费注册的要早

那么是这个原因么? 静待下次源码分析

II. 其他

0. 项目

1. 一灰灰Blog

尽信书则不如,以上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激

下面一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛

一灰灰blog


打赏 如果觉得我的文章对您有帮助,请随意打赏。
分享到