spring模块(六)spring监听器(3)广播与异步问题

news2024/11/23 3:15:43

发布事件和监听器之间默认是同步的;监听器则是广播形式。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 {


    @Resource
    private 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) {
        //触发listener
        UserLogDTO 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 {

    @EventListener
    public void myEventListener(MyLogEvent event) {
        UserLogDTO source = event.getSource();
        log.info("监听到:url={},detail={}",source.getUrl(),source.getDetail());
        //其他处理,比如存储日志
    }


    @EventListener
    public 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加入异常代码

 @EventListener
    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());
    }

接口调用也异常

对于事件监听器(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() {
        @Override
        public 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 {

    @Resource
    private 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) {
        //触发listener
        UserLogDTO 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 {

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


    @EventListener
    public 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.coloradmin.cn/o/2134885.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

界面控件DevExpress中文教程:如何PDF图形对象的可见性?

DevExpress拥有.NET开发需要的所有平台控件,包含600多个UI控件、报表平台、DevExpress Dashboard eXpressApp 框架、适用于 Visual Studio的CodeRush等一系列辅助工具。屡获大奖的软件开发平台DevExpress 近期重要版本v24.1已正式发布,该版本拥有众多新产…

数据资产盘点

数据资产盘点包含调研诊断、数据盘点、数据对标校正、分类分级、权责划分、数据资产目录建立六大环节。调研诊断:通常采用访谈或案头梳理的方式,对 IT 整体建设情况、业务系统数据情况进行调研,框定数据资产管理范围、聚焦目标。 数据盘点&a…

spring整合mabatis框架(druid连接池)

spring整合mabatis框架&#xff0c;duird连接池&#xff0c;Junit5测试框架 1&#xff09;创建Maven工程 2&#xff09;导入相关的依赖 <!--springContext依赖--><dependency><groupId>org.springframework</groupId><artifactId>spring-contex…

helm一键化部署pod

目录 概念 安装helm helm的命令 自定义模版 回滚 概念 helm提供了一个模版&#xff0c;可以一键化的部署微服务。它通过打包的方式&#xff0c;把所有需要的yaml文件集合一起&#xff0c;然后一键部署&#xff0c;还可以支持回滚。 helm的本质&#xff1a;就是可以把k8s…

低压电抗器与电容器安装距离

低压电抗器与电容器的安装距离是一个关键问题&#xff0c;主要考虑电气安全、热效应以及电磁干扰等因素。通常建议保持适当的安装距离以确保设备的正常运行和安全性。 以下是一些常见的参考原则&#xff1a; 1、热效应 电抗器和电容器在运行过程中都会产生热量。如果两者之间距…

代数模型(Algebraic Models)---线性规划------ + 案例 + Python源码求解(见文中)

目录 一、代数模型&#xff08;Algebraic Models&#xff09;详解1.1什么是代数模型&#xff1f;1.2代数模型的基本形式1.3 安装所需要的Python包--运行下述案例1.4代数模型的应用案例案例 1&#xff1a;市场供需平衡模型Python求解代码Python求解结果如下图&#xff1a; 案例 …

GDPU MySQL数据库 天码行空1 数据库的创建和基本操作

一、实验目的 1&#xff0e;熟知机房用机安全规则。 2&#xff0e;通过上机操作&#xff0c;加深对数据库系统理论知识的理解&#xff1b;通过使用具体的DBMS&#xff0c;了解一种实际的数据库管理系统&#xff0c;并掌握其操作技术&#xff1b;通过对实际题目的上机实验&…

Java8的函数式编程简介

文章目录 环境背景方法方法1&#xff1a;Java 7&#xff08;传统方法&#xff09;方法2&#xff1a;Java 7 &#xff08;策略模式&#xff09;方法3&#xff1a;Java 8的Lambda表达式方法4&#xff1a;Java 8内建的函数式接口Predicate方法5&#xff1a;Java 8的方法引用方法6&…

JavaSE:5、类与对象

1、类的定义与对象的创建 定义属性 创建对象 2、对象的使用 使用一个变量来指代某个对象&#xff0c;只不过引用类型的变量&#xff0c;存储的是对象的引用&#xff0c;而不是对象本身 public class Main {public static void main(String [] argv){Person p1new Person();P…

Oracle发送邮件功能:配置自动化发信指南?

Oracle发送邮件服务设置方法&#xff1f;怎么用Oracle数据库发信&#xff1f; Oracle数据库作为企业级应用的核心&#xff0c;其内置的发送邮件功能为企业提供了强大的自动化工具。AokSend将详细介绍如何配置Oracle发送邮件功能&#xff0c;以实现自动化发信&#xff0c;从而提…

C和指针:指针

内存和地址 程序视角看内存是一个大的字节数组&#xff0c;每个字节包含8个位&#xff0c;可以存储无符号值0至255,或有符号值-128至127。 多个字节可以合成一个字&#xff0c;许多机器以字为单位存储整数&#xff0c;每个字一般由2个或4个字节组成。 由于它们包含了更多的位&…

react native(expo)多语言适配

项目基于 expo框架 开发。请先配置好 expo 开发环境 1.引入i18n-js npx expo install i18n-js 2.新建languages文件夹&#xff0c;其中包括英文、中文等语种目录。结构如下&#xff1a; *.json文件为语种翻译后的json键值对&#xff0c;用于UI中引用; { "appName&q…

【C语言】(指针系列3)数组指针+函数指针+typedef+函数数组指针+转移表

前言&#xff1a;前言&#xff1a;开始之前先感谢一位大佬&#xff0c;清风~徐~来-CSDN博客&#xff0c;由于是时间久远&#xff0c;博主指针的系列忘的差不多了&#xff0c;所以有顺序部分借鉴了该播主的&#xff0c;同时也加入了博主自己的理解&#xff0c;有些地方如果解释的…

MySQL语句案例编写复习

先看我的表数据和结构 1.查询年龄为16,17,18,19岁的女性员工信息。 select * from emp where gender 女 and age in(16,17,18,19); 2.查询性别为 男 &#xff0c;并且年龄在 20-40 岁(含)以内的姓名为三个字的员工。 select * from emp where gender 男 and age between …

猫罐头多久喂一次?营养健康的罐头推荐

一&#xff0e;猫罐头多久喂一次 猫咪长期只食用干粮&#xff0c;容易饮水不足&#xff0c;从而引发上尿道或膀胱结石、堵塞等问题&#xff0c;所以最好每周喂至少2个猫罐头&#xff0c;帮助猫咪补充水分。如果条件允许&#xff0c;全罐喂养&#xff0c;每天都给猫咪吃猫罐头是…

车机中 Android Audio 音频常见问题分析方法实践小结

文章目录 前言1. 无声2. 断音3. 杂音4. 延迟播放5. 焦点问题6. 无声问题(连上 BT )其他完善中…… 前言 本文主要总结了一下车机开发中遇到的 Audio 有关的问题&#xff0c;同时参考网上的一案例&#xff0c;由于Audio 模块出现音频问题的场景很多&#xff0c;对每一个出现的问…

Blender渲染太慢怎么办?blender云渲染已开启

动画行业蓬勃发展&#xff0c;动画制作软件亦持续推陈出新&#xff0c;当制作平台日益丰富&#xff0c;创作难度降低&#xff0c;创作效率提升&#xff0c;如何高效完成复杂动画的渲染就成了从业者更关心的问题。 云渲染技术的出现&#xff0c;无疑为动画制作者提供了前所未有…

家庭理财管理系统

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1 、功能描述 家庭理财管理系统拥有多种角色&#xff0c;可以自行设置权限和用户等&#xff0c;主要功能有&#xff1a; 收入管理、支付管理、资产管理、负债详情、统计报表、家庭成员管理、用户管理等…

JavaSE - 易错题集 - 006

1. 哪个正确 A abstract类只能用来派生子类&#xff0c;不能用来创建abstract类的对象。 B final类不但可以用来派生子类&#xff0c;也可以用来创建final类的对象。 C abstract不能与final同时修饰一个类。 D abstract类定义中可以没有abstract方法。 正确答案&#xff1…

决策树算法上篇

决策树概述 决策树是属于有监督机器学习的一种&#xff0c;起源非常早&#xff0c;符合直觉并且非常直观&#xff0c;模仿人类做决策的过程&#xff0c;早期人工智能模型中有很多应用&#xff0c;现在更多的是使用基于决策树的一些集成学习的算法。 示例一&#xff1a; 上表根据…