聊聊Spring事件机制

一、前言

在我们平时的开发工作当中,肯定有这样一些业务场景,这块业务完成后,就给用户推送一些消息以提醒用户,比如给用户发送一条下单成功信息,对于这种非核心的业务功能其实我们可以单独拿出来,创建一个事件去异步执行,这样子就可以实现核心业务和子业务的解耦。

二、具体实现步骤

  1. 首先定义一个事件类Event

    java 复制代码
    public class NoticeEvent extends ApplicationEvent {
        private String message;
    
        public NoticeEvent(String message) {
            super(message);
            this.message = message;
        }
    
        public String getMessage() {
            return message;
        }
    }

    所有事件类都需要继承ApplicationEvent

创建事件监听类
java 复制代码
@Component
public class NoticeListener implements ApplicationListener<NoticeEvent> {

    private static final Logger logger = LoggerFactory.getLogger(NoticeListener.class);

    @Override
    public void onApplicationEvent(NoticeEvent event) {
        try {
            logger.info("接收到的消息:{}",event);
            //模拟处理消息
            Thread.sleep(5000);
            logger.info("处理消息完成:{}",event);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

该类用于监听指定事件的类,需要实现ApplicationListener接口。注意这里需要加上@Component注解,将其注入Spring容器中。
3. 发布事件

java 复制代码
@RestController
@RequestMapping("/event")
public class EventController {
    private static final Logger logger = LoggerFactory.getLogger(EventController.class);
    @Autowired
    private ApplicationEventPublisher publisher;

    @GetMapping("/notice/{message}")
    public void notice(@PathVariable(name = "message")String message) {
        logger.info("开始发送消息:{}",message);
        publisher.publishEvent(new NoticeEvent(message));
        logger.info("消息发送完成");
    }
}

使用Spring 提供的ApplicationEventPublisher来发布自定义事件。

代码写完了,我们直接来测试看效果:

启动服务,访问接口:http://localhost:8003/batch/event/notice/testMessage

我们可以看到,从始至终都是同一条线程去完成这个任务的,并没有异步去执行。

从这里我们可以得知,默认是没有开启异步的,我们需要手动配置开启异步功能,我们需要在启动类中加上@EnableAsync注解,该注解用于声明启用Spring的异步方法执行功能。然后我们在监听方法上加上@Async注解,说明当前方法使用异步去执行。

然后测试:

从截图可以看出,已经实现了异步功能,主线程为http-nio-8003-exec-1,监听线程为 task-1。从日志打印可以看出,接口直接返回了,并没有等监听线程执行完后才返回。

这里我们再提一点,开启异步后,会使用Spring内部默认的线程池,我们可以自定义这个线程池,如下:

java 复制代码
@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {

    private final Logger log = LoggerFactory.getLogger(this.getClass());

    /**
     * 自定义异步线程池,若不重写会使用默认的线程池
     */
    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        // 核心线程数
        taskExecutor.setCorePoolSize(2);
        // 最大线程数
        taskExecutor.setMaxPoolSize(10);
        // 队列大小
        taskExecutor.setQueueCapacity(15);
        // 线程名的前缀
        taskExecutor.setThreadNamePrefix("async-thread-");
        taskExecutor.initialize();
        return taskExecutor;
    }
}

测试:

已经使用上我们自定义的线程池了。

好了,关于Spring中的ApplicationEvent和ApplicationListener我们就介绍这么多。

相关推荐
Mintopia2 小时前
🚪 当 Next.js 中间件穿上保安制服:请求拦截与权限控制的底层奇幻之旅
前端·后端·next.js
DKPT2 小时前
JVM栈溢出时如何dump栈信息?
java·jvm·笔记·学习·spring
DKPT2 小时前
JVM堆大小如何设置?
java·开发语言·jvm·笔记·学习
铅笔侠_小龙虾2 小时前
JVM 目录
java·jvm
yunxi_052 小时前
让大模型会“说话”:基于 Spring WebSocket 的毫秒级流式 RAG 对话
java·后端
用户6120414922132 小时前
jsp+servlet做的医院挂号看诊管理系统
java·javascript·mysql
€8112 小时前
Java入门级教程21——Java 缓存技术、RMI远程方法调用、多线程分割大文件
java·开发语言·java缓存代理模式的实现·java rmi远程方法调用·多线程分割大文件
渣哥2 小时前
Java线程池那些坑:我与线程池的恩怨情仇
java
hour_go2 小时前
BPEL:企业流程自动化的幕后指挥家
java·运维·自动化