欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Springboot事件監(jiān)聽與@Async注解詳解

 更新時(shí)間:2024年01月10日 10:29:57   作者:苦糖果與忍冬  
這篇文章主要介紹了Springboot事件監(jiān)聽與@Async注解詳解,在開發(fā)中經(jīng)??梢岳肧pring事件監(jiān)聽來實(shí)現(xiàn)觀察者模式,進(jìn)行一些非事務(wù)性的操作,如記錄日志之類的,需要的朋友可以參考下

一、不求甚解

在開發(fā)中經(jīng)??梢岳肧pring事件監(jiān)聽來實(shí)現(xiàn)觀察者模式,進(jìn)行一些非事務(wù)性的操作,如記錄日志之類的。

Controller

Controller中注入ApplicationEventPublisher,并利用它發(fā)布事件即可。

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("請(qǐng)求進(jìn)來了");
        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實(shí)現(xiàn)ApplicationListener接口即可。或者使用@EventListener注解

接口方式

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

注解方式

import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
@Component
@Async
public class TestListener {
    @EventListener
    public void onApplicationEvent(TestEvent testEvent) {
        System.out.println("TestListener:"+Thread.currentThread().getName());
        System.out.println("TestListener:"+testEvent.getName());
    }
}

Postman測試 //localhost:8080/controller/test?name=lisi

在這里插入圖片描述

控制臺(tái)打?。?/p>

請(qǐng)求進(jìn)來了
lisi

曾經(jīng),我一直以為這樣就實(shí)現(xiàn)了異步,因?yàn)槲铱垂镜拇a都是這樣寫的,現(xiàn)網(wǎng)也沒什么問題。每次照著前輩們的代碼CV一下就可以了,也沒太多想。

二、人云亦云

@Async注解

以前一直用@Async注解,但我好像沒有去深入了解過她。使用她就像使用@Resource一樣自然一樣隨心所欲。

直到某天閑了下來,突發(fā)奇想,想要深入了解一下她。

@Async注解很容易踩坑,首先是必須在啟動(dòng)類上開啟異步配置@EnableAsync才行,否則還是同步執(zhí)行。如果不指定線程池,則使用Spring默認(rèn)的線程池 SimpleAsyncTaskExecutor。

方法上一旦標(biāo)記了@Async注解,當(dāng)其它線程調(diào)用這個(gè)方法時(shí),就會(huì)開啟一個(gè)新的子線程去異步處理該業(yè)務(wù)邏輯。

1)在方法上使用該@Async注解,申明該方法是一個(gè)異步任務(wù);在類上面使用該@Async注解,申明該類中的所有方法都是異步任務(wù);

2)使用此注解的方法的類對(duì)象,必須是Spring管理下的bean對(duì)象; 所以需要在類上加上@Component注解。

3)要想使用異步任務(wù),需要在主類上開啟異步配置,即,配置上@EnableAsync注解; 

4)如果不指定線程池的名稱,則使用Spring默認(rèn)的線程池SimpleAsyncTaskExecutor。

SimpleAsyncTaskExecutor特性:

  • 1)為每個(gè)任務(wù)啟動(dòng)一個(gè)新線程,異步執(zhí)行它。
  • 2)支持通過“concurrencyLimit” bean 屬性限制并發(fā)線程。默認(rèn)情況下,并發(fā)線程數(shù)是無限的。
  • 3)注意:此實(shí)現(xiàn)不重用線程!
  • 4)考慮一個(gè)線程池 TaskExecutor 實(shí)現(xiàn),特別是用于執(zhí)行大量短期任務(wù)。

以上內(nèi)容是我百度后所得,到底對(duì)不對(duì)呢?我決定親自驗(yàn)證一下。

所以,使用@Async注解的時(shí)候一定要指定自己的線程池?。。?/p>

在啟動(dòng)類沒有指定注解@EnableAsync的情況下,測試一下打印出當(dāng)前的線程

 @GetMapping("/test")
    public String test(@RequestParam String name){
        System.out.println("TestController請(qǐng)求進(jìn)來了:"+Thread.currentThread().getName());
        TestEvent event = new TestEvent(this,name);
        applicationEventPublisher.publishEvent(event);
        System.out.println("TestController請(qǐng)求出去了:"+Thread.currentThread().getName());
        return "success";
    }
    @Override
    public void onApplicationEvent(TestEvent testEvent) {
        System.out.println("TestListener:"+Thread.currentThread().getName());
        System.out.println("TestListener:"+testEvent.getName());
    }

在這里插入圖片描述

根據(jù)上述結(jié)果可證明在沒有開啟異步配置@EnableAsync的情況下還是同步執(zhí)行!

三、刨根問底

Springboot中異步默認(rèn)使用的線程池真的是SimpleAsyncTaskExecutor嗎???

在不指定線程池的情況下,debug查看spring中異步默認(rèn)的線程池。

開啟異步 在啟動(dòng)類上添加該注解 @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實(shí)例,實(shí)現(xiàn)這個(gè)接口即可: 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請(qǐng)求進(jìn)來了:"+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請(qǐng)求出去了:"+Thread.currentThread().getName());
        // 根據(jù)class對(duì)象獲取spring管理的bean實(shí)例
        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中打上斷點(diǎn)。

在這里插入圖片描述

當(dāng)前spring版本為5.2.12,可能與spring版本有關(guān),當(dāng)前版本的線程池默認(rèn)是ThreadPoolTaskExecutor??梢钥吹骄€程池最大容量是Integer的最大值,在高并發(fā)場景下可能導(dǎo)致OOM。因此需要自定義線程池,并在@Async上指定為自定義線程池。

打印出了spring中所管理的bean的名稱,applicationTaskExecutor果然也在。

在這里插入圖片描述

打印出線程池的前綴,再一次驗(yàn)證確認(rèn)線程池默認(rèn)是ThreadPoolTaskExecutor。

在這里插入圖片描述

四、曲突徙薪

最近寫了一個(gè)接口,要求響應(yīng)時(shí)間1s以內(nèi),并且請(qǐng)求量很大,明確要求一些非重要業(yè)務(wù)的操作都必須異步操作。

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

保持好奇,富有探索,始終對(duì)技術(shù)有敏感性?。?!

自定義線程池

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;
    }
}

在異步注解上標(biāo)明使用自定義線程池 @Async(“MyExecutor”)

Jmeter壓測

在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

壓測結(jié)果表明確實(shí)切換了線程池,使用了自定義的線程池,并且阻塞隊(duì)列已滿,開始朝著最大線程數(shù)邁進(jìn)!

到此這篇關(guān)于Springboot事件監(jiān)聽與@Async注解詳解的文章就介紹到這了,更多相關(guān)Springboot監(jiān)聽與@Async內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 你知道怎么從Python角度學(xué)習(xí)Java基礎(chǔ)

    你知道怎么從Python角度學(xué)習(xí)Java基礎(chǔ)

    這篇文章主要為大家詳細(xì)介紹了Python角度學(xué)習(xí)Java基礎(chǔ)的方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-02-02
  • 一文秒懂java到底是值傳遞還是引用傳遞

    一文秒懂java到底是值傳遞還是引用傳遞

    這篇文章主要介紹了java到底是值傳遞還是引用傳遞的相關(guān)知識(shí),本文通過幾個(gè)例子給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-06-06
  • Java利用Picocli開發(fā)一個(gè)簡化命令行工具

    Java利用Picocli開發(fā)一個(gè)簡化命令行工具

    Picocli 是一個(gè)強(qiáng)大、易用且功能豐富的 Java 庫,用于開發(fā)命令行工具,本文我們就來為大家介紹一下Java如何利用Picocli進(jìn)行命令行簡化功能的吧
    2025-03-03
  • 詳解如何保護(hù)SpringBoot配置文件中的敏感信息

    詳解如何保護(hù)SpringBoot配置文件中的敏感信息

    使用過SpringBoot配置文件的朋友都知道,資源文件中的內(nèi)容通常情況下是明文顯示,安全性就比較低一些,所以為了提高安全性,就需要對(duì)配置文件中的敏感信息進(jìn)行保護(hù),下面就為大家介紹一下實(shí)現(xiàn)方法吧
    2023-07-07
  • Spring數(shù)據(jù)庫連接池實(shí)現(xiàn)原理深入刨析

    Spring數(shù)據(jù)庫連接池實(shí)現(xiàn)原理深入刨析

    開發(fā)web項(xiàng)目,我們肯定會(huì)和數(shù)據(jù)庫打交道,因此就會(huì)涉及到數(shù)據(jù)庫鏈接的問題。在以前我們開發(fā)傳統(tǒng)的SSM結(jié)構(gòu)的項(xiàng)目時(shí)進(jìn)行數(shù)據(jù)庫鏈接都是通過JDBC進(jìn)行數(shù)據(jù)鏈接,我們每和數(shù)據(jù)庫打一次交道都需要先獲取一次鏈接,操作完后再關(guān)閉鏈接,這樣子效率很低,因此就出現(xiàn)了連接池
    2022-11-11
  • MultipartFile中transferTo(File file)的路徑問題及解決

    MultipartFile中transferTo(File file)的路徑問題及解決

    這篇文章主要介紹了MultipartFile中transferTo(File file)的路徑問題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • Springboot項(xiàng)目對(duì)數(shù)據(jù)庫用戶名密碼實(shí)現(xiàn)加密過程解析

    Springboot項(xiàng)目對(duì)數(shù)據(jù)庫用戶名密碼實(shí)現(xiàn)加密過程解析

    這篇文章主要介紹了Springboot項(xiàng)目對(duì)數(shù)據(jù)庫用戶名密碼實(shí)現(xiàn)加密過程解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-06-06
  • java實(shí)現(xiàn)賬號(hào)登錄時(shí)發(fā)送郵件通知

    java實(shí)現(xiàn)賬號(hào)登錄時(shí)發(fā)送郵件通知

    這篇文章主要為大家詳細(xì)介紹了java如何實(shí)現(xiàn)在賬號(hào)登錄時(shí)發(fā)送郵件通知的功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2023-09-09
  • springboot3.X版本集成mybatis遇到的問題及解決

    springboot3.X版本集成mybatis遇到的問題及解決

    在將SpringBoot3.X版本與MyBatis集成時(shí),直接參考基于SpringBoot2.X的配置方法會(huì)導(dǎo)致各種報(bào)錯(cuò),尤其是無法注入mapper的bean問題,這主要是因?yàn)镾pringBoot3.X版本需要搭配MyBatis3.0.3及以上版本才能正常工作,通過更新maven配置至MyBatis3.0.3版本,可以解決這一問題
    2024-09-09
  • 詳解Java分布式IP限流和防止惡意IP攻擊方案

    詳解Java分布式IP限流和防止惡意IP攻擊方案

    這篇文章主要介紹了詳解Java分布式IP限流和防止惡意IP攻擊方案,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-03-03

最新評(píng)論