SpringBoot下使用自定義監(jiān)聽(tīng)事件的流程分析
事件機(jī)制是Spring的一個(gè)功能,目前我們使用了SpringBoot框架,所以記錄下事件機(jī)制在SpringBoot框架下的使用,同時(shí)實(shí)現(xiàn)異步處理。事件機(jī)制其實(shí)就是使用了觀察者模式(發(fā)布-訂閱模式)。
Spring的事件機(jī)制經(jīng)過(guò)如下流程:
- 1、自定義事件,繼承org.springframework.context.ApplicationEvent抽象類(lèi)
- 2、定義事件監(jiān)聽(tīng)器,實(shí)現(xiàn)org.springframework.context.ApplicationListener接口
- 3、在Spring容器中發(fā)布事件
SpringBoot的實(shí)例程序
實(shí)現(xiàn)一個(gè)保存用戶的時(shí)候,向用戶提供的郵箱發(fā)送一封郵件的功能,同時(shí)采用異步處理。
自定義事件
import org.springframework.context.ApplicationEvent;
public class EmailEvent extends ApplicationEvent {
private static final long serialVersionUID = 3733891603598996786L;
private String emailAddress;
public EmailEvent(String emailAddress) {
super(emailAddress);
this.emailAddress = emailAddress;
}
public String getEmailAddress() {
return emailAddress;
}
public void setEmailAddress(String emailAddress) {
this.emailAddress = emailAddress;
}
}定義事件監(jiān)聽(tīng)器
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
@Component
public class EmailEventListener implements ApplicationListener<EmailEvent> {
private static Logger log = LoggerFactory.getLogger(EmailEventListener.class);
// 異步處理
@Async
@Override
public void onApplicationEvent(EmailEvent event) {
log.info("監(jiān)聽(tīng)到事件--郵箱地址:" + event.getEmailAddress());
//模擬處理的耗時(shí)3s
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("事件處理完成");
}
}發(fā)布事件
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
@Component
public class EmailEventPublish {
@Autowired
private ApplicationContext applicationContext;
public void publishEvent(String emailAddress) {
EmailEvent event = new EmailEvent(emailAddress);
applicationContext.publishEvent(event);
}
}調(diào)用事件
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.example.demo.event.EmailEventPublish;
@RestController
public class EventController {
private static Logger log = LoggerFactory.getLogger(EventController.class);
@Autowired
private EmailEventPublish emailEventPublish;
@RequestMapping("/event")
public void publishEvent(@RequestParam String emailAddress) {
// 發(fā)布事件 -- 采用異步處理
emailEventPublish.publishEvent(emailAddress);
// 正常該語(yǔ)句先執(zhí)行
log.info("Controller業(yè)務(wù)處理");
}
}結(jié)果
訪問(wèn)如下地址
http://localhost:8080/event?emailAddress=plf@163.com
結(jié)果為
2023-08-04 21:21:14.338 INFO 6400 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring FrameworkServlet 'dispatcherServlet'
2023-08-04 21:21:14.338 INFO 6400 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization started
2023-08-04 21:21:14.370 INFO 6400 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization completed in 32 ms
2023-08-04 21:21:14.429 INFO 6400 --- [nio-8080-exec-1] .s.a.AnnotationAsyncExecutionInterceptor : No task executor bean found for async processing: no bean of type TaskExecutor and no bean named 'taskExecutor' either
2023-08-04 21:21:14.534 INFO 6400 --- [nio-8080-exec-1] c.e.demo.controller.EventController : Controller業(yè)務(wù)處理
2023-08-04 21:21:14.535 INFO 6400 --- [cTaskExecutor-1] c.example.demo.event.EmailEventListener : 監(jiān)聽(tīng)到事件--郵箱地址:plf@163.com
2023-08-04 21:21:17.536 INFO 6400 --- [cTaskExecutor-1] c.example.demo.event.EmailEventListener : 事件處理完成
上述結(jié)果可知是實(shí)現(xiàn)了異步處理,先打印了事件之后的程序,等時(shí)間到再執(zhí)行監(jiān)聽(tīng)程序的代碼。
實(shí)現(xiàn)異步處理就是在監(jiān)聽(tīng)事件執(zhí)行業(yè)務(wù)代碼的方法上添加 @Async 注解,同時(shí)在啟動(dòng)類(lèi)上添加 @EnableAsync 即可。
上面的日志還提到了 TaskExecutor ,這是如果有自定義的線程池就會(huì)去調(diào)用,如果沒(méi)有就用默認(rèn)的。我們也可以自己定義一個(gè) TaskExecutor 。
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.lang.Nullable;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
@EnableAsync
@Configuration
public class ThreadPool implements AsyncConfigurer {
@Nullable
@Override
@Bean("taskExecutor")
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 線程池創(chuàng)建時(shí)候初始化的線程數(shù)
executor.setCorePoolSize(10);
// 線程池最大的線程數(shù),只有在緩沖隊(duì)列滿了之后才會(huì)申請(qǐng)超過(guò)核心線程數(shù)的線程
executor.setMaxPoolSize(20);
// 用來(lái)緩沖執(zhí)行任務(wù)的隊(duì)列
executor.setQueueCapacity(200);
// 允許線程的空閑時(shí)間60秒
executor.setKeepAliveSeconds(60);
// 線程池名的前綴
executor.setThreadNamePrefix("taskExecutor-");
// 線程池對(duì)拒絕任務(wù)的處理策略
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
@Nullable
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return null;
}
}結(jié)果
2023-08-04 21:27:36.507 INFO 7848 --- [nio-8080-exec-2] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring FrameworkServlet 'dispatcherServlet'
2023-08-04 21:27:36.507 INFO 7848 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization started
2023-08-04 21:27:36.537 INFO 7848 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization completed in 30 ms
2023-08-04 21:27:36.757 INFO 7848 --- [nio-8080-exec-2] c.e.demo.controller.EventController : Controller業(yè)務(wù)處理
2023-08-04 21:27:36.757 INFO 7848 --- [ taskExecutor-1] c.example.demo.event.EmailEventListener : 監(jiān)聽(tīng)到事件--郵箱地址:plf@163.com
2023-08-04 21:27:39.757 INFO 7848 --- [ taskExecutor-1] c.example.demo.event.EmailEventListener : 事件處理完成
可知是使用我們定義的線程池[ taskExecutor-1] 。
總結(jié)
Spring的事件機(jī)制是一個(gè)很實(shí)用的一個(gè)功能,在監(jiān)聽(tīng)和異步處理相關(guān)的功能比較適合。
到此這篇關(guān)于SpringBoot下使用自定義監(jiān)聽(tīng)事件的文章就介紹到這了,更多相關(guān)SpringBoot自定義監(jiān)聽(tīng)事件內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring Boot + MyBatis Plus 高效開(kāi)發(fā)實(shí)戰(zhàn)從入
本文將詳細(xì)介紹 Spring Boot + MyBatis Plus 的完整開(kāi)發(fā)流程,并深入剖析分頁(yè)查詢、批量操作、動(dòng)態(tài) SQL、樂(lè)觀鎖、代碼優(yōu)化等實(shí)戰(zhàn)技巧,感興趣的朋友一起看看吧2025-04-04
SpringBoot配置lombok與logback過(guò)程解析
這篇文章主要介紹了SpringBoot配置lombok與logback過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-05-05
SpringBoot多環(huán)境配置數(shù)據(jù)讀取方式
SpringBoot通過(guò)環(huán)境隔離機(jī)制,支持properties/yaml/yml多格式配置,結(jié)合@Value、Environment和@ConfigurationProperties實(shí)現(xiàn)靈活配置讀取,下面給大家介紹SpringBoot之多環(huán)境配置全解析,感興趣的朋友一起看看吧2025-07-07
Spring實(shí)現(xiàn)上拉刷新和下拉加載效果
這篇文章主要為大家詳細(xì)介紹了Spring實(shí)現(xiàn)上拉刷新和下拉加載效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12
java 數(shù)據(jù)結(jié)構(gòu)基本算法希爾排序
這篇文章主要介紹了數(shù)據(jù)結(jié)構(gòu)基本算法希爾排序的相關(guān)資料,需要的朋友可以參考下2017-08-08

