一灰灰blog 一灰灰blog
首页
  • InfluxDB
  • MongoDB
  • MySql
  • 基础系列
  • DB系列
  • 搜索系列
  • MQ系列
  • WEB系列
  • 中间件
  • 运维
  • SpringSecurity
  • SpringCloud
  • QuickAlarm
  • QuickCrawer
  • QuickFix
  • QuickMedia
  • QuickSpi
  • QuickTask
  • 高可用
  • 分类
  • 标签
  • 归档
  • 收藏
  • 关于
GitHub (opens new window)

一灰灰blog

资深搬运工
首页
  • InfluxDB
  • MongoDB
  • MySql
  • 基础系列
  • DB系列
  • 搜索系列
  • MQ系列
  • WEB系列
  • 中间件
  • 运维
  • SpringSecurity
  • SpringCloud
  • QuickAlarm
  • QuickCrawer
  • QuickFix
  • QuickMedia
  • QuickSpi
  • QuickTask
  • 高可用
  • 分类
  • 标签
  • 归档
  • 收藏
  • 关于
GitHub (opens new window)
  • 基础系列

    • 配置

    • AOP

    • Bean

    • SpEL

    • 事件

      • 【基础系列】事件机制的两种消费姿势
        • I. 项目环境
        • II. 事件机制
          • 1. 事件对象
          • 2. 接口方式消费
          • 3. 注解方式消费
          • 4. 发布事件
          • 5. 测试
        • II. 其他
          • 0. 项目
          • 1. 一灰灰Blog
    • 国际化

    • 定时器

    • 日志

  • DB系列

  • 搜索系列

  • MQ系列

  • WEB系列

  • 中间件

  • 运维

  • SpringSecurity

  • SpringCloud

  • Spring系列
  • 基础系列
  • 事件
一灰灰
2021-04-29

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

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

# I. 项目环境

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

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

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

# II. 事件机制

# 1. 事件对象

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

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 + '\'' +
                '}';
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 2. 接口方式消费

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

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

# 3. 注解方式消费

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

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

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

# 4. 发布事件

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

@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));
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 5. 测试

一个简单的测试demo

@RestController
public class IndexController {
    @Autowired
    private MsgPublisher msgPublisher;

    @GetMapping(path = "pub")
    public String publish(String msg) {
        msgPublisher.publish(msg);
        return "ok";
    }
}
1
2
3
4
5
6
7
8
9
10
11

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

输出日志:

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

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

@SpringBootApplication
public class Application {

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

}
1
2
3
4
5
6
7
8
9
10
11

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

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

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

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

# II. 其他

# 0. 项目

  • 工程:https://github.com/liuyueyi/spring-boot-demo (opens new window)
  • 源码:https://github.com/liuyueyi/spring-boot-demo/blob/master/spring-boot/012-context-listener/ (opens new window)

# 1. 一灰灰Blog

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

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

  • 一灰灰Blog个人博客 https://blog.hhui.top (opens new window)
  • 一灰灰Blog-Spring专题博客 http://spring.hhui.top (opens new window)

一灰灰blog

编辑 (opens new window)
#EventListener
上次更新: 2021/10/15, 19:56:22
【基础系列】SpEL语法扫盲与查询手册
【基础系列】SpringBoot 国际化支持实例开发

← 【基础系列】SpEL语法扫盲与查询手册 【基础系列】SpringBoot 国际化支持实例开发→

最近更新
01
【基础系列】基于maven多环境配置
04-25
02
【WEB系列】内嵌Tomcat配置Accesslog日志文件生成位置源码探索
04-24
03
【搜索系列】ES查询常用实例演示
04-18
更多文章>
Theme by Vdoing | Copyright © 2017-2022 一灰灰Blog
MIT License | 鄂ICP备18017282号 |
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×