IT俱乐部 Java springboot publish event 事件机制demo分享

springboot publish event 事件机制demo分享

1. 使用ApplicationEventPublisher 发布事件

复制下面全部代码,右键包名,粘贴即可生成java类,执行即可看到效果。

事件机制

  • 需要自定义一个事件类继承ApplicationEvent;
  • 需要自定义一个监听器类实现ApplicationListener接口,或普通类的方法中使用@EventListener注解;
  • 使用默认发布器ApplicationEventPublisher发布即可;
  • 事件类不需要注入到IOC;监听器需要注入到IOC;ApplicationEventPublisher用Autowired注入进来即可;
  • 默认情况下,事件发布与执行是同步的,事件执行完毕,发布者才会执行下面的逻辑;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
package com.example.controller;
 
import com.alibaba.fastjson.JSON;
import com.example.SpringbootRedisApplication;
import lombok.Data;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Component;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
 
import java.util.Objects;
 
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = SpringbootRedisApplication.class)
@Slf4j
@EnableAsync
public class PublishEventDemo {
    /**
     * 事件机制:
     * 需要自定义一个事件类继承ApplicationEvent;
     * 需要自定义一个监听器类实现ApplicationListener接口,或普通类的方法中使用@EventListener注解;
     * 使用默认发布器ApplicationEventPublisher发布即可;
     * 事件类不需要注入到IOC;监听器需要注入到IOC;ApplicationEventPublisher用Autowired注入进来即可;
     */
 
 
    /**
     * 事件发布器
     */
    @Autowired
    private ApplicationEventPublisher eventPublisher;
 
    /**
     * 测试类
     */
    @Test
    public void publishTest() throws InterruptedException {
        Task task = new Task();
        task.setId(1L);
        task.setTaskName("测试任务");
        task.setTaskContext("任务内容");
        task.setFinish(false);
        MyEvent event = new MyEvent(task);
        log.info("开始发布任务");
        eventPublisher.publishEvent(event);
        //applicationContext.publishEvent(event);
        log.info("结束发布任务");
        Thread.sleep(10000);
    }
}
 
/**
 * 任务类
 * 任务就是一个普通的类,用来保存你要发布事件的内容。这个没有特殊限制,可以根据自己业务随意设置。
 */
@Data
class Task {
 
    private Long id;
 
    private String taskName;
 
    private String taskContext;
 
    private boolean finish;
}
 
/**
 * 事件类
 * 事件类需要继承org.springframework.context.ApplicationEvent,这样发布的事件才能被Spring所识别
 */
@Slf4j
class MyEvent extends ApplicationEvent {
 
    private Task task;
 
    public MyEvent(Task task) {
        super(task);
        this.task = task;
    }
 
    public Task getTask() {
        return task;
    }
}
 
/**
 * 事件监听类
 * 事件的监听器需要实现org.springframework.context.ApplicationListener,并且需要注入到容器之中。
 */
@Component
@Slf4j
class MyEventListenerA implements ApplicationListener {
    /**
     * 监听方式1:实现ApplicationListener接口,重写onApplicationEvent方法
     * 需要使用Component注入IOC
     */
    @SneakyThrows
    @Async
    @Override
    public void onApplicationEvent(MyEvent MyEvent) {
        Thread.sleep(5000);
        if (Objects.isNull(MyEvent)) {
            return;
        }
        Task task = MyEvent.getTask();
        log.info("监听器A接收任务:{}", JSON.toJSONString(task));
        task.setFinish(true);
        log.info("监听器A此时完成任务");
 
    }
}
 
@Component
@Slf4j
class MyEventListenerB implements ApplicationListener {
    /**
     * 监听方式2:接口和注解混合使用
     * 但此时 @EventListener不能与注解@Async在同一个类中使用,会报错,至于为什么,不知道;
     * 需要使用Component注入IOC
     */
    //@Async//加上这个,@EventListener的方法就会报java.lang.IllegalStateException: Failed to load ApplicationContext
    @SneakyThrows
    @Override
    public void onApplicationEvent(MyEvent MyEvent) {
        Thread.sleep(1000);
        if (Objects.isNull(MyEvent)) {
            return;
        }
        Task task = MyEvent.getTask();
        log.info("监听器B接收任务:{}", JSON.toJSONString(task));
        task.setFinish(true);
        log.info("监听器B此时完成任务");
    }
 
    @EventListener
    public void someMethod(MyEvent event) throws InterruptedException {
        Thread.sleep(1000);
        log.info("监听器@EventListenerB收到={}", event.getTask());
    }
}
 
 
@Component
@Slf4j
class MyEventListennerC {
    /**
     * 监听方式3:注解@EventListener的监听器不需要实现任何接口
     * 需要使用Component注入IOC
     */
    @EventListener
    public void someMethod(MyEvent event) throws InterruptedException {
        Thread.sleep(1000);
        log.info("监听器@EventListenerC收到={}", event.getTask());
    }
}

运行日志:

2020-11-30 18:13:56.238  INFO 19776 — [           main] com.example.controller.PublishEventDemo  : Started PublishEventDemo in 11.098 seconds (JVM running for 13.737)
2020-11-30 18:13:56.902  INFO 19776 — [           main] com.example.controller.PublishEventDemo  : 开始发布任务
2020-11-30 18:13:57.904  INFO 19776 — [           main] com.example.controller.MyEventListenerB  : 监听器@EventListenerB收到=Task(id=1, taskName=测试任务, taskContext=任务内容, finish=false)
2020-11-30 18:13:58.905  INFO 19776 — [           main] c.example.controller.MyEventListennerC   : 监听器@EventListenerC收到=Task(id=1, taskName=测试任务, taskContext=任务内容, finish=false)
2020-11-30 18:13:59.920  INFO 19776 — [           main] com.example.controller.MyEventListenerB  : 监听器B接收任务:{“finish”:false,”id”:1,”taskContext”:”任务内容”,”taskName”:”测试任务”}
2020-11-30 18:13:59.921  INFO 19776 — [           main] com.example.controller.MyEventListenerB  : 监听器B此时完成任务
2020-11-30 18:13:59.921  INFO 19776 — [           main] com.example.controller.PublishEventDemo  : 结束发布任务
2020-11-30 18:14:03.913  INFO 19776 — [         task-1] com.example.controller.MyEventListenerA  : 监听器A接收任务:{“finish”:true,”id”:1,”taskContext”:”任务内容”,”taskName”:”测试任务”}
2020-11-30 18:14:03.913  INFO 19776 — [         task-1] com.example.controller.MyEventListenerA  : 监听器A此时完成任务
2020-11-30 18:14:09.958  INFO 19776 — [       Thread-3] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService ‘applicationTaskExecutor’

事件发送后,会等待事件执行完毕,因此他们是同步的。若想异步执行事件,可以把@Async加到监听方法上;

2. 使用ApplicationContext发布事件

与上例不同之处

  • 使用ApplicationContext发布事件,ApplicationContext实现了ApplicationEventPublisher接口;
  • 使用ApplicationContextEvent 定义事件,ApplicationContextEvent 继承了ApplicationEvent类;

demo代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
package com.example.controller;
 
import ch.qos.logback.classic.Logger;
import com.example.SpringbootRedisApplication;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ApplicationContextEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.web.bind.annotation.RestController;
 
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = SpringbootRedisApplication.class)
@RestController
@Slf4j
public class PublishEventDemo2 {
    /**
     * 使用ApplicationContext发布事件
     */
    @Autowired
    ApplicationContext applicationContext;
 
    @Test
    public void send() {
        MyEvent2 myEvent2 = new MyEvent2(applicationContext);
        myEvent2.setData("数据");
        log.info("开始发送事件");
        applicationContext.publishEvent(myEvent2);
        log.info("结束发送事件");
    }
}
 
/**
 * 监听器类
 */
@Slf4j
@Component
class MyEventListener2 implements ApplicationListener {
    @Override
    public void onApplicationEvent(MyEvent2 event) {
        log.info("监听器MyEventListener2收到={}", event);
    }
 
    @EventListener
    public void someMethod(MyEvent2 event) {
        log.info("监听器MyEventListener2@EventListener收到={}", event);
    }
}
 
/**
 * 监听器类
 */
@Slf4j
@Component
class MyEventListenner1 {
    @EventListener
    public void someMethod(MyEvent2 event) {
        log.info("监听器MyEventListenner1收到={}", event);
    }
}
 
/**
 * 事件类
 *
 * @param
 */
@ToString
class MyEvent2 extends ApplicationContextEvent {
    private T data;
 
    public MyEvent2(ApplicationContext source) {
        super(source);
    }
 
    public T getData() {
        return data;
    }
 
    public void setData(T data) {
        this.data = data;
    }
}

运行日志:

2020-11-30 18:03:38.638  INFO 15792 — [           main] c.example.controller.PublishEventDemo2   : Started PublishEventDemo2 in 9.571 seconds (JVM running for 12.677)
2020-11-30 18:03:39.355  INFO 15792 — [           main] c.example.controller.PublishEventDemo2   : 开始发送事件
2020-11-30 18:03:39.358  INFO 15792 — [           main] com.example.controller.MyEventListener2  : 监听器MyEventListener2@EventListener收到=MyEvent2(data=数据)
2020-11-30 18:03:39.359  INFO 15792 — [           main] c.example.controller.MyEventListenner1   : 监听器MyEventListenner1收到=MyEvent2(data=数据)
2020-11-30 18:03:39.359  INFO 15792 — [           main] com.example.controller.MyEventListener2  : 监听器MyEventListener2收到=MyEvent2(data=数据)
2020-11-30 18:03:39.359  INFO 15792 — [           main] c.example.controller.PublishEventDemo2   : 结束发送事件
2020-11-30 18:03:39.435  INFO 15792 — [       Thread-3] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService ‘applicationTaskExecutor’

事件发送后,会等待事件执行完毕,因此他们是同步的。若想异步执行事件,可以把@Async加到监听方法上;

以上为个人经验,希望能给大家一个参考,也希望大家多多支持IT俱乐部。

本文收集自网络,不代表IT俱乐部立场,转载请注明出处。https://www.2it.club/code/java/6873.html
上一篇
下一篇
联系我们

联系我们

在线咨询: QQ交谈

邮箱: 1120393934@qq.com

工作时间:周一至周五,9:00-17:30,节假日休息

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部