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

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 {


    @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.kler.cn/a/307117.html

相关文章:

  • 【Elasticsearch入门到落地】1、初识Elasticsearch
  • 【CICD】GitLab Runner 和执行器(Executor
  • 【go从零单排】Rate Limiting限流
  • 行业类别-智能制造-子类别工业4.0-细分类别物联网应用-应用场景智能工厂建设
  • ManiSkill学习笔记
  • FPGA高速设计之Aurora64B/66B的应用与不足的修正
  • 鸿蒙OS 资源文件
  • 七、结合Landsat、夜光数据建成区提取——K均值聚类和监督分类提取精确的建成区边界
  • AI为云游戏带来的革新及解决方案:深度技术剖析与未来展望
  • windows下自启springboot项目(jar+nginx)
  • 安卓网址自动添加%,显示网页异常
  • IAPP发布《2024年人工智能治理实践报告》
  • 【Leetcode:1184. 公交站间的距离 + 模拟】
  • 【2025】基于python的网上商城比价系统、智能商城比价系统、电商比价系统、智能商城比价系统(源码+文档+解答)
  • Ready Go
  • 本地部署大语言模型
  • 6. Fabric 拖拽元素到画布
  • 聊聊OceanBase合并和转储
  • 2024.9最新:CUDA安装,pytorch库安装
  • 大数据之Spark(二)
  • 开题报告的流程
  • 农产品自动识别系统(Java+Springboot+SSM+Vue+Maven+二维码溯源+识别农作物CNN模型PyTorch框架)
  • Java 枚举 新特性
  • 20240912软考架构-------软考161-165答案解析
  • matlab delsat = setdiff(1:69,unique(Eph(30,:))); 语句含义
  • firewalld中ipset与zone的区别