日常开发中,我们有时会使用SpringEvent对业务解耦,使我们的代码更加高内聚低耦合,不过如果对其运行原理不清楚,那么在使用的过程中,一不留神就会出现一些bug 。
今天我们回顾一下SpringEvent使用的基本原理,需要优化的点,以及非常常见的两种错误 。
1,基本原理
![当我准备用SpringEvent优雅的解耦时,连续两个bug把我搞懵了](http://img.jiangsulong.com/221208/1IHU5b-0.png)
文章插图
Spring的事件模式其实很简单,我们创建一个Event事件,当Event发生时,广播器对事件进行发布,然后对应的Listener进行处理即可 。
Spring的事件一共有三个组件:
1,Event:用于定于我们的事件,比如ApplicationEvent或者通过继承ApplicationEvent定义我们自己的事件 。
2,广播器Multicaster:当事件发生时,将事件广播出去 。
3,监听器Listener:监听和处理广播器广播的事件 。
2,基本用法第一步,首先定义一个Event事件,
@Getter@Setterpublic class MessageEvent extends ApplicationEvent {private String content;public MessageEvent(String content) {super(new Object());this.content = content;}}
第二步,定义一个Listener对事件进行监听,@Componentpublic class MessageListener {@EventListenerpublic void listen(MessageEvent messageEvent) {System.out.println("收到消息:" + messageEvent.getContent());}}
最后在我们的业务逻辑需要的地方,就可以发布事件了 。@RestController@RequestMapping(value = https://www.isolves.com/it/cxkf/kj/2022-12-08/"/demo")public class DemoController {@Resourceprivate ApplicationContext applicationContext;@PostMapping(value = "/send")public ResponseEntity sendMessage() {//.....//处理一些业务逻辑之后,发送通知消息MessageEvent messageEvent = new MessageEvent("发布一条测试消息");this.applicationContext.publishEvent(messageEvent);return ResponseEntity.ok().build();}}
3,需要注意的点一,对于同一个Event,我们可以定义多个Listener,多个Listener之间可以通过@Order来指定顺序,order的Value值越小,执行的优先级就越高 。二,我们可以使用@EventListener轻松标记一个方法作为监听器,但是默认情况下,它是同步执行的,所以如果发布事件的方法处于事务中,那么事务会在监听器方法执行完毕之后才提交 。
有些情况下,我们希望事件发布之后就由监听器去处理,而不要影响原有的事务,也就是说希望事务及时提交 。
这时我们可以使用@
TransactionalEventListener来定义一个监听器 。
@Componentpublic class MessageListener {//上层事务执行完毕之后再执行@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT, fallbackExecution = true)public void listen(MessageEvent messageEvent) {System.out.println(Thread.currentThread().getName());System.out.println("收到消息:" + messageEvent.getContent());}}
三,默认情况下,@EventListener定义的方法是同步执行的,如果我们想通过异步的方式执行一个监听器的方法,可以在方法上加上@Async注解(记得在启动类上加上@EnableAsync开启异步执行配置) 。需要注意的是,使用@Async时,必须为其配置线程池,否则用的还是默认的线程 。
如@Async(value = https://www.isolves.com/it/cxkf/kj/2022-12-08/"taskExecutor"),此时Listener就会被分配到taskExecutor的线程池中执行 。
【当我准备用SpringEvent优雅的解耦时,连续两个bug把我搞懵了】使用@Async异步执行的同时,还会带来另外两个问题,需要大家注意:
1,如果Listener执行过程中抛出了异常,由于是异步执行,异常并不会被事件发布方捕获 。
2,异步执行时,方法的返回值不能用来发布后续事件,如果需要处理结果去发布另一个事件,需要我们手动去发布 。
4,常见错误一:错误的监听一个并不会抛出的事件有时我们希望监听Spring的启动事件,做一些初始化操作 。于是有的同学可能定义了这样一个Listener:
@Componentpublic class MessageListener {@EventListenerpublic void listen2(ContextStartedEvent event) {System.out.println("Spring启动了," + event.toString());}}
不过,虽然名字看起来似乎是一个上下文启动时的事件,但是Spring启动时并不会发布这个事件,我们启动项目看下控制台是否会打印日志:![当我准备用SpringEvent优雅的解耦时,连续两个bug把我搞懵了](http://img.jiangsulong.com/221208/1IHW401-1.png)
文章插图
可以看到,Spring项目启动后,并没有打印任何日志 。
其实Spring项目启动后发布的真正Event是ContextRefreshedEvent,我们修改下代码再看一下结果:
![当我准备用SpringEvent优雅的解耦时,连续两个bug把我搞懵了](http://img.jiangsulong.com/221208/1IHVX5-2.png)
推荐阅读
- 人体的标准正常体温是多少?什么情况下才算是发烧?不妨了解一下
- 宋轶|宋轶与白敬亭恋情后续!亲妈手滑点赞准女婿,网传双方见过父母
- 毕业论文标准格式要求是什么样的? 论文的标准格式
- iPhone XS Max续航逆天,借助备用电量可多玩手机5小时
- 民事诉讼费收取标准 诉讼费收取办法
- 业务招待费税前扣除比例是什么? 招待费扣除标准
- 歌词说说大全 说说歌词
- 咸肉怎么做好吃蒸咸肉 咸肉怎么做好吃
- 车险理赔流程及索赔单证行业标准 车险理赔流程图
- 求职|如何精准地找工作