Springboot事件监听+@Async注解

news2025/1/24 1:38:51

文章目录

      • 一、不求甚解
      • 二、人云亦云
      • 三、刨根问底
      • 四、曲突徙薪

一、不求甚解

在开发中经常可以利用Spring事件监听来实现观察者模式,进行一些非事务性的操作,如记录日志之类的。

Controller

Controller中注入ApplicationEventPublisher,并利用它发布事件即可。

import org.springframework.context.ApplicationEventPublisher;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
@RequestMapping("/controller")
public class TestController {

    @Resource
    private ApplicationEventPublisher applicationEventPublisher;

    @GetMapping("/test")
    public String test(@RequestParam String name){
        System.out.println("请求进来了");
        TestEvent event = new TestEvent(this,name);
        applicationEventPublisher.publishEvent(event);
        return "success";
    }
}

Event

event继承ApplicationEvent即可。

import org.springframework.context.ApplicationEvent;

public class TestEvent extends ApplicationEvent {

    private String name;

    public TestEvent(Object source,String name) {
        super(source);
        this.name=name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Listener

Listener实现ApplicationListener接口即可。

import org.springframework.context.ApplicationListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

@Component
@Async
public class TestListener implements ApplicationListener<TestEvent> {
    @Override
    public void onApplicationEvent(TestEvent testEvent) {
        System.out.println(testEvent.getName());
    }
}

Postman测试
http://localhost:8080/controller/test?name=lisi
在这里插入图片描述
控制台打印:

请求进来了
lisi

曾经,我一直以为这样就实现了异步,因为我看公司的代码都是这样写的,现网也没什么问题。每次照着前辈们的代码CV一下就可以了,也没太多想。

二、人云亦云

@Async注解

以前一直用@Async注解,但我好像没有去深入了解过她。使用她就像使用@Resource一样自然一样随心所欲。

直到某天闲了下来,突发奇想,想要深入了解一下她。

@Async注解很容易踩坑,首先是必须在启动类上开启异步配置@EnableAsync才行,否则还是同步执行。如果不指定线程池,则使用Spring默认的线程池 SimpleAsyncTaskExecutor。 方法上一旦标记了@Async注解,当其它线程调用这个方法时,就会开启一个新的子线程去异步处理该业务逻辑。

    1)在方法上使用该@Async注解,申明该方法是一个异步任务;在类上面使用该@Async注解,申明该类中的所有方法都是异步任务;
    2)使用此注解的方法的类对象,必须是Spring管理下的bean对象; 所以需要在类上加上@Component注解。
    3)要想使用异步任务,需要在主类上开启异步配置,即,配置上@EnableAsync注解; 
    4)如果不指定线程池的名称,则使用Spring默认的线程池SimpleAsyncTaskExecutor。

SimpleAsyncTaskExecutor特性:

1)为每个任务启动一个新线程,异步执行它。
2)支持通过“concurrencyLimit” bean 属性限制并发线程。默认情况下,并发线程数是无限的。
3)注意:此实现不重用线程!
4)考虑一个线程池 TaskExecutor 实现,特别是用于执行大量短期任务。

以上内容是我百度后所得,到底对不对呢?我决定亲自验证一下。

所以,使用@Async注解的时候一定要指定自己的线程池!!!

在启动类没有指定注解@EnableAsync的情况下,测试一下打印出当前的线程

 @GetMapping("/test")
    public String test(@RequestParam String name){
        System.out.println("TestController请求进来了:"+Thread.currentThread().getName());
        TestEvent event = new TestEvent(this,name);
        applicationEventPublisher.publishEvent(event);
        System.out.println("TestController请求出去了:"+Thread.currentThread().getName());
        return "success";
    }
    @Override
    public void onApplicationEvent(TestEvent testEvent) {
        System.out.println("TestListener:"+Thread.currentThread().getName());
        System.out.println("TestListener:"+testEvent.getName());
    }

在这里插入图片描述

根据上述结果可证明在没有开启异步配置@EnableAsync的情况下还是同步执行!

三、刨根问底

Springboot中异步默认使用的线程池真的是SimpleAsyncTaskExecutor吗???

源码不会看,看也看不懂。菜鸡只会debug!!!

菜是原罪!!!
在这里插入图片描述

在不指定线程池的情况下,debug查看spring中异步默认的线程池。

开启异步
在启动类上添加该注解 @EnableAsync

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;

@SpringBootApplication
@EnableAsync
public class TestApplication {

    public static void main(String[] args) {
        SpringApplication.run(TestApplication.class, args);
    }
}

获取spring管理的bean实例,实现这个接口即可: ApplicationContextAware

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.util.CustomizableThreadCreator;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
@RequestMapping("/controller")
public class TestController implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    @Resource
    private ApplicationEventPublisher applicationEventPublisher;

    @GetMapping("/test")
    public String test(@RequestParam String name){
        System.out.println("TestController请求进来了:"+Thread.currentThread().getName());
        TestEvent event = new TestEvent(this,name);
        // 获取spring管理的所有的bean的名称
        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        for (int i = 0; i <beanDefinitionNames.length ; i++) {
            System.out.println(beanDefinitionNames[i]);
        }
        applicationEventPublisher.publishEvent(event);
        System.out.println("TestController请求出去了:"+Thread.currentThread().getName());
        // 根据class对象获取spring管理的bean实例
        CustomizableThreadCreator bean = applicationContext.getBean(CustomizableThreadCreator.class);
        System.out.println("CustomizableThreadCreator:"+bean.getThreadNamePrefix());
        return "success";
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        TestController.applicationContext=applicationContext;
    }
}

在TestListener中打上断点。

在这里插入图片描述当前spring版本为5.2.12,可能与spring版本有关,当前版本的线程池默认是ThreadPoolTaskExecutor。可以看到线程池最大容量是Integer的最大值,在高并发场景下可能导致OOM。因此需要自定义线程池,并在@Async上指定为自定义线程池。

打印出了spring中所管理的bean的名称,applicationTaskExecutor果然也在。
在这里插入图片描述打印出线程池的前缀,再一次验证确认线程池默认是ThreadPoolTaskExecutor。
在这里插入图片描述

四、曲突徙薪

最近写了一个接口,要求响应时间1s以内,并且请求量很大,明确要求一些非重要业务的操作都必须异步操作。

按着以前的套路,我还是照着上面一操作了一遍。还好今天研究了一下,否则以后的某天怕是要背一口大锅。

保持好奇,富有探索,始终对技术有敏感性!!!

自定义线程池

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;

@Configuration
public class MyExecutorConfig {

    @Bean("MyExecutor")
    public Executor myExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10);
        executor.setMaxPoolSize(50);
        executor.setQueueCapacity(2000);
        executor.setKeepAliveSeconds(60);
        executor.setThreadNamePrefix("myExecutor");
        executor.setRejectedExecutionHandler( new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }
}

在异步注解上标明使用自定义线程池
@Async(“MyExecutor”)

Jmeter压测
在这里插入图片描述在这里插入图片描述在这里插入图片描述

压测结果表明确实切换了线程池,使用了自定义的线程池,并且阻塞队列已满,开始朝着最大线程数迈进!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/105245.html

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

相关文章

内科大软件工程导论复习内容笔记

文章目录一、 概念&#xff08;共60分&#xff0c;单选题、多选题&#xff09;1. 软件是程序、数据、文档的集合。2. 软件的分类&#xff08;按规模、性质分&#xff09;3. 软件危机的表现、缓解软件危机的途径4. 软件工程方法学主要有两种&#xff1a;结构化的软件工程方法学_…

借助 Material Design,帮助您打造更好的无障碍应用 (上篇)

随着时代的发展&#xff0c;"无障碍体验" 对开发者的意义也愈发重大。我们准备了无障碍设计文章&#xff0c;帮助您为更多用户打造精彩应用。本文是第一篇内容&#xff0c;将为您介绍辅助技术&#xff0c;层次结构&#xff0c;颜色和对比度等内容。了解无障碍改进产品…

[前端攻坚]:Set的使用

总结一些日常需要用到的一些api&#xff0c;也是在一些面试中会经常出现的题目&#xff0c;今天分享的是Set的一些用法和个人的一些理解&#xff0c; 同时文章也被收录到我的《JS基础》专栏中&#xff0c;欢迎大家点击收藏加关注。 我们知道Set是一种叫做集合的数据结构&#x…

手把手教你成为荣耀开发者:如何获取平台帮助及最新通知?

荣耀开发者服务平台是荣耀面向开发者的统一生态入口&#xff0c;通过聚合周边内外部系统&#xff0c;分全球多站点部署&#xff0c;为全球开发者提供业务全生命周期的商业支撑服务&#xff0c;拥有应用分发、智慧服务、开放能力、HONOR Connect等众多业务等您来合作。 在与荣耀…

引擎入门 | Unity UI简介–第3部分(2)

本期我们继续为大家进行【Unity UI简介–第3部分】的后续教程 学习内容 4.添加遮挡面板 5.添加内容面板 6.添加按钮 文章末尾可免费获取教程源代码 本篇【Unity UI简介–第3部分】篇幅较长&#xff0c;分为八篇&#xff0c;本篇为第二篇&#xff0c;还未看过第一篇的请点…

Linux学习-89-JDK下载安装(rpm方式)

17.19 JDK下载安装&#xff08;rpm方式&#xff09; linux系统上jdk安装方式比较多&#xff0c;最简单的便是yum安装&#xff0c;可以使用以下步骤来安装&#xff1a; #首先先检查是否已经安装JDK [rootCncLucZK ~]# rpm -qa |grep java[rootCncLucZK ~]# rpm -qa |grep jdk …

Docker ( 三 ) 安装 MySQL

6.2.安装 MySQL 6.2.1.拉取镜像 6.2.1.1.搜索查看可用的mysql镜像 docker search mysql6.2.1.2.拉取最新版本的mysql镜像 docker pull mysql:latest6.2.1.3.查看镜像是否已经有mysql docker images|grep mysql6.2.2.启动容器 6.2.2.1.启动容器实例 启动同时设置名字以及r…

搜遍全网,终于找到了报表自动化的最佳工具,比Excel好用10倍

工作快十年了&#xff0c;最开始是在华为做报表&#xff0c;后来去了美团从事大数据和平台方面的工作&#xff0c;现在在国企干了快三年。辗转各大企业的IT部门&#xff0c;最明显的感知就是企业间数字化程度差距太大了。就从报表这个点来说&#xff0c;互联网公司早就实现了报…

动物宠物领养网站的设计与实现(Vue+Spring Boot+Java)

目 录 摘 要 I Abstract II 目 录 III 图清单 V 表清单 VII 1 绪论 1 1.1 动物领养网站的现状与发展 1 1.2 “万千宠爱”动物领养网站的研究内容 2 1.3 “万千宠爱”动物领养网站的研究目的和意义 2 1.4 本章小结 3 2 本“万千宠爱”动物领养网站的分析 4 2.1 可行性分析 4 2.…

【目标检测】YOLOv5能识别英雄和小兵?原理解析~

目录 一、简介 二、模型结构 1.整体结构图 2.Backbone&#xff08;CSPDarknet&#xff09; 3.SPPF&#xff08;Spatial Pyramid Pooling - Fast&#xff09; 4.Neck&#xff08;FPNPAN&#xff09; 5.Head 三、anchor编解码 1.anchor编码 2.anchor解码 四、损失函数 …

【20天快速掌握Python】day09-模块和包

1.Python中的模块 在Python中有一个概念叫做模块&#xff08;module&#xff09;。 说的通俗点&#xff1a;模块就好比是工具包&#xff0c;要想使用这个工具包中的工具(就好比函数)&#xff0c;就需要导入这个模块 比如我们经常使用工具 random&#xff0c;就是一个模块。使…

车险java开发工程师【10k-15k】13薪

众推职聘”以交付结果为宗旨的全流程化招聘服务平台&#xff01; 今日招聘信息↓ 【工作内容】 1.参与软件项目和产品概要设计&#xff0c;负责详细功能设计、编码实现及相关文档编写&#xff1b; 2.根据模块设计完成相应的模块编码及单元测试&#xff1b; 3.对用户行为、需求…

「React 深入」知悉Fiber,方能百战不殆~

在React v16以上的版本引入了一个非常重要的概念&#xff0c;那就是fiber&#xff0c;实际上fiber是react团队花费两年的时间重构的架构&#xff0c;在之前的文章中也提及到了fiber&#xff0c;那么fiber架构究竟是什么&#xff0c;为什么要使用fiber 在正式开始前&#xff0c…

网络空间安全——MS15_034漏洞验证与安全加固

网络空间安全——MS15_034漏洞验证与安全加固 靶机&#xff1a;windows 2008 安装好iis7.5 1.安装iis7.5,用127.0.0.1访问&#xff0c;将访问页面截图 2.用burpsuite 抓包分析是否存在ms15_034漏洞&#xff0c;抓包验证截图 3.调用msf相应的测试模块进行扫描&#xff0c;…

沥高科技冲刺创业板:拟募资5.45亿 为胡仲杰与岑婵芳夫妻店

雷递网 雷建平 12月20日上海沥高科技股份有限公司&#xff08;简称&#xff1a;“沥高科技”&#xff09;日前递交招股书&#xff0c;准备在深交所创业板上市。沥高科技计划募资5.45亿元&#xff0c;其中&#xff0c;1.79亿元用于航空航天用真空袋工艺材料生产项目&#xff0c;…

Talk预告 | 悉尼科技大学在读博士生胡思逸:MARLlib,全新的多智能体强化学习框架

本期为TechBeat人工智能社区第464期线上Talk&#xff01; 北京时间12月21日(周三)20:00&#xff0c;悉尼科技大学澳大利亚人工智能研究所ReLER实验室在读博士生——胡思逸的Talk将准时在TechBeat人工智能社区开播&#xff01; 他与大家分享的主题是: “MARLlib, 全新的多智能体…

2022 年 MathorCup 高校数学建模挑战赛A题

赛道 A&#xff1a;“58 到家”家政服务订单分配问题 “58 到家”是“58 同城”旗下高品质、高效率的上门家政服务平台&#xff0c;平 台向用户提供家政保洁、保姆、月嫂、搬家、维修等众多生活领域的服务。 在家政保洁场景中&#xff0c;用户在平台下单购买服务后&#xff0c;…

非零基础自学Golang 第13章 并发与通道 13.3 channel 13.3.1 channel类型 13.3.2 缓冲机制

非零基础自学Golang 文章目录非零基础自学Golang第13章 并发与通道13.3 channel13.3.1 channel类型13.3.2 缓冲机制第13章 并发与通道 13.3 channel goroutine运行在相同的地址空间&#xff0c;因此访问共享内存必须做好同步。 引用类型channel是CSP模式的具体体现&#xff…

游戏玩得好的AI,已经在看病救人了

一个游戏AI&#xff0c;怎么干起医生的活了&#xff1f; 而且这本事还是从打游戏的经验里总结来的。 喏&#xff0c;拿一张病理全片扫描图像&#xff0c;不用遍历所有高倍镜视野&#xff0c;也能找到病灶所在。 在它看来&#xff0c;这个过程和《我的世界》里伐木居然是类似的…

Diffusion惊艳应用大赏

文&#xff5c;白鹡鸰自从Diffusion模型兴起之后&#xff0c;AI绘画圈又迎来了一波猪突猛进式的强化&#xff0c;早几年还只是Ins的二次元/迪士尼风格滤镜&#xff0c;让人穿上不同服装当接头霸王。现在&#xff0c;你随便输几句话&#xff0c;模型就能刷刷刷给你吐出一大堆精美…