当前位置: 首页 > news >正文

spring模块(六)spring event事件(3)广播与异步问题

发布事件和监听器之间默认是同步的;监听器则是广播形式。demo:

 event:

package com.listener.demo.event;import com.listener.demo.dto.UserLogDTO;
import org.springframework.context.ApplicationEvent;public class MyLogEvent extends ApplicationEvent {public MyLogEvent(UserLogDTO log) {super(log);}public UserLogDTO getSource() {return (UserLogDTO) super.getSource();}}

producer:

@RestController
@RequestMapping("/user")
@Slf4j
public class UserController {@Resourceprivate ApplicationContext applicationContext;@MyLog(url = "/user/add",detail = "addUser")@RequestMapping("/add")public String add(UserDTO userDTO) {this.notifyEvent(userDTO);log.info("请求成功,返回");return "add success";}private void notifyEvent(UserDTO userDTO) {//触发listenerUserLogDTO userLogDTO = UserLogDTO.builder().detail("新增"+userDTO.getUserAccount()).url("/user/add").build();applicationContext.publishEvent(new MyLogEvent(userLogDTO));}@MyLog(url = "/user/update",detail = "updateUser")@RequestMapping("/update")public String update() {return "update success";}
}

监听器:

package com.listener.demo.listener;import com.listener.demo.dto.UserLogDTO;
import com.listener.demo.event.MyLogEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;@Slf4j
@Component
public class MyListenerOne {@EventListenerpublic void myEventListener(MyLogEvent event) {UserLogDTO source = event.getSource();log.info("监听到:url={},detail={}",source.getUrl(),source.getDetail());//其他处理,比如存储日志}@EventListenerpublic void contextRefreshedEventListener(ContextRefreshedEvent event) {log.info("监听到内置事件ContextRefreshedEvent...");}
}

目录

一、广播 

二、监听器异常

三、验证同步和异步

1、默认同步

2、异步


一、广播 

对于同一个Event,我们可以定义多个Listener,多个Listener之间可以通过@Order来指定顺序,order的Value值越小,执行的优先级就越高。

下面对同一个事件加上多个监听器,copy MyListenerOne为MyListenerTwo。访问接口日志打印:

2024-07-29T09:48:14.818+08:00  INFO 46376 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port 4444 (http) with context path '/listenerDemo'
2024-07-29T09:48:14.824+08:00  INFO 46376 --- [           main] c.listener.demo.listener.MyListenerOne   : 监听到内置事件ContextRefreshedEvent...
2024-07-29T09:48:14.824+08:00  INFO 46376 --- [           main] c.listener.demo.listener.MyListenerTwo   : 监听到内置事件ContextRefreshedEvent...
2024-07-29T09:48:14.825+08:00  INFO 46376 --- [           main] com.listener.demo.ListenerApplication    : Started ListenerApplication in 1.222 seconds (process running for 1.678)
2024-07-29T09:48:22.619+08:00  INFO 46376 --- [nio-4444-exec-1] o.a.c.c.C.[.[localhost].[/listenerDemo]  : Initializing Spring DispatcherServlet 'dispatcherServlet'
2024-07-29T09:48:22.619+08:00  INFO 46376 --- [nio-4444-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2024-07-29T09:48:22.620+08:00  INFO 46376 --- [nio-4444-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 0 ms
2024-07-29T09:48:22.646+08:00  INFO 46376 --- [nio-4444-exec-1] c.l.demo.controller.UserController       : 请求成功,返回
2024-07-29T09:48:28.656+08:00  INFO 46376 --- [         task-1] c.listener.demo.listener.MyListenerOne   : 监听到:url=/user/add,detail=新增zs
2024-07-29T09:48:28.656+08:00  INFO 46376 --- [         task-2] c.listener.demo.listener.MyListenerTwo   : 监听到:url=/user/add,detail=新增zs

可以看到多个listener都监听到了,是广播的形式。 

二、监听器异常

在某一个Listener加入异常代码

 @EventListenerpublic void myEventListener(MyLogEvent event) throws InterruptedException {//下游业务处理//Thread.sleep(6000);int a =  1/0;UserLogDTO source = event.getSource();log.info("监听到:url={},detail={}",source.getUrl(),source.getDetail());}

接口调用也异常

对于事件监听器(EventListener)抛出异常导致接口异常,可以采取以下几种策略来解决:

1、监听器加异常处理

在事件监听器中添加try-catch块来捕获并处理可能发生的异常

@EventListener
public void handleEvent(SomeEvent event) {try {// 事件处理逻辑} catch (Exception e) {// 记录日志或者进行其他处理}
}
2、阻止异常抛出

使用@TransactionalEventListener时,设置fallbackExecution属性为truefalse来控制在事件监听器抛出异常时的行为。

@TransactionalEventListener(fallbackExecution = true)
public void handleEvent(SomeEvent event) {// 事件处理逻辑
}
3、使用ApplicationEventMulticaster的事件传播策略来控制事件监听器的异常行为。

@Autowired
private ApplicationEventMulticaster multicaster;@PostConstruct
public void setTaskExecutionListenerMulticaster() {multicaster.setErrorHandler(new ErrorHandler() {@Overridepublic void handleError(Throwable t) {// 处理异常}});
}
4、异步

使用@Async注解来异步执行事件监听器,从而避免监听器内的异常影响主线程。

@Async
@EventListener
public void handleEvent(SomeEvent event) {// 事件处理逻辑
}

三、验证同步和异步

1、默认同步

触发event,监听器和调用处是同步执行的,调用处-->listen执行-->调用处;

package com.listener.demo.controller;import com.listener.demo.annotation.MyLog;
import com.listener.demo.dto.UserDTO;
import com.listener.demo.dto.UserLogDTO;
import com.listener.demo.event.MyLogEvent;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/user")
@Slf4j
public class UserController {@Resourceprivate ApplicationContext applicationContext;/*@MyLog(url = "/user/add",detail = "addUser")*/@RequestMapping("/add")public String add(UserDTO userDTO) {this.notifyEvent(userDTO);log.info("请求成功,返回");return "add success";}private void notifyEvent(UserDTO userDTO) {//触发listenerUserLogDTO userLogDTO = UserLogDTO.builder().detail("新增"+userDTO.getUserAccount()).url("/user/add").build();applicationContext.publishEvent(new MyLogEvent(userLogDTO));}@MyLog(url = "/user/update",detail = "updateUser")@RequestMapping("/update")public String update() {return "update success";}
}
package com.listener.demo.listener;import com.listener.demo.dto.UserLogDTO;
import com.listener.demo.event.MyLogEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;@Slf4j
@Component
public class MyListenerOne {@EventListenerpublic void myEventListener(MyLogEvent event) throws InterruptedException {//下游业务处理Thread.sleep(6000);UserLogDTO source = event.getSource();log.info("监听到:url={},detail={}",source.getUrl(),source.getDetail());}@EventListenerpublic void contextRefreshedEventListener(ContextRefreshedEvent event) {log.info("监听到内置事件ContextRefreshedEvent...");}
}

调用接口到返回的时间很长,日志打印

2024-07-29T09:42:02.161+08:00  INFO 29800 --- [nio-4444-exec-7] c.listener.demo.listener.MyListenerOne   : 监听到:url=/user/add,detail=新增zs
2024-07-29T09:42:02.161+08:00  INFO 29800 --- [nio-4444-exec-7] c.l.demo.controller.UserController       : 请求成功,返回
2、异步

 如果需要异步执行,需要单独加上异步代码:

package com.listener.demo;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;@EnableAsync
@SpringBootApplication
public class ListenerApplication {public static void main(String[] args) {SpringApplication.run(ListenerApplication.class, args);}
}
  @EventListener@Async()public void myEventListener(MyLogEvent event) throws InterruptedException {//下游业务处理Thread.sleep(6000);UserLogDTO source = event.getSource();log.info("监听到:url={},detail={}",source.getUrl(),source.getDetail());}

再次访问打印

2024-07-29T09:45:00.049+08:00  INFO 49128 --- [nio-4444-exec-3] c.l.demo.controller.UserController       : 请求成功,返回
2024-07-29T09:45:06.059+08:00  INFO 49128 --- [         task-1] c.listener.demo.listener.MyListenerOne   : 监听到:url=/user/add,detail=新增zs

  这时候在某一个监听器加入异常代码:

@EventListener@Async()public void myEventListener(MyLogEvent event) throws InterruptedException {//下游业务处理//Thread.sleep(6000);int a =  1/0;UserLogDTO source = event.getSource();log.info("监听到:url={},detail={}",source.getUrl(),source.getDetail());}

 接口可以正常访问

日志打印这一个监听器报错

http://www.lryc.cn/news/439728.html

相关文章:

  • 【Elasticsearch系列八】高阶使用
  • 【H2O2|全栈】关于CSS(4)CSS基础(四)
  • node.js+Koa框架+MySQL实现注册登录
  • 矢量化操作
  • 【LeetCode】每日一题 2024_9_16 公交站间的距离(模拟)
  • 【Python笔记】PyCharm大模型项目环境配置
  • FPGA-Vivado-IP核-虚拟输入输出(VIO)
  • 使用knn算法对iris数据集进行分类
  • GEE Shapefile 格式转换 GeoJSON
  • 从kaggle竞赛零基础上手CV实战(Deepfake检测)
  • Linux cat命令详解使用:高效文本内容管理
  • YOLOv9改进系列,YOLOv9颈部网络SPPELAN替换为FocalModulation
  • 圆环加载效果
  • leetcode - 分治思想
  • Java面试题·解释题·单例模式、工厂模式、代理模式部分
  • 如何编写智能合约——基于长安链的Go语言的合约开发
  • 【PHP代码审计】PHP基础知识
  • 大模型笔记03--快速体验dify
  • Linux常用命令以及操作技巧
  • C语言 | Leetcode C语言题解之题409题最长回文串
  • FreeSql 全面指南:从基础到高级实战,深入解析读写分离与导航属性
  • 深度学习之微积分预备知识点
  • 动态内存
  • C/C++实现植物大战僵尸(PVZ)(打地鼠版)
  • C++ 科目二 智能指针 [weak_ptr] (解决shared_ptr的循环引用问题)
  • 解决RabbitMQ设置TTL过期后不进入死信队列
  • 【鸿蒙OH-v5.0源码分析之 Linux Kernel 部分】005 - Kernel 入口 C 函数 start_kernel() 源码分析
  • EndnoteX9安装及使用教程
  • SQL:子查询
  • C语言刷题日记(附详解)(5)