发布事件和监听器之间默认是同步的;监听器则是广播形式。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属性为true或false来控制在事件监听器抛出异常时的行为。
@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());
    } 
接口可以正常访问

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



















