springboot publish event 事件机制demo分享
作者:阿拉的梦想
1. 使用ApplicationEventPublisher 发布事件
复制下面全部代码,右键包名,粘贴即可生成java类,执行即可看到效果。
事件机制
- 需要自定义一个事件类继承ApplicationEvent;
- 需要自定义一个监听器类实现ApplicationListener接口,或普通类的方法中使用@EventListener注解;
- 使用默认发布器ApplicationEventPublisher发布即可;
- 事件类不需要注入到IOC;监听器需要注入到IOC;ApplicationEventPublisher用Autowired注入进来即可;
- 默认情况下,事件发布与执行是同步的,事件执行完毕,发布者才会执行下面的逻辑;
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<T> 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<MyEvent> { /** * 监听方式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<MyEvent> { /** * 监听方式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代码:
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<MyEvent2> { @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 <T> */ @ToString class MyEvent2<T> 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加到监听方法上;
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。