spring中ApplicationListener的使用小結(jié)
ApplicationListener 是spring提供的一個監(jiān)聽器,它可以實現(xiàn)一個簡單的發(fā)布-訂閱功能,用有點外行但最簡單通俗的話來解釋:監(jiān)聽到主業(yè)務(wù)在執(zhí)行到了某個節(jié)點之后,在監(jiān)聽器里面做出相應(yīng)的其它業(yè)務(wù)變更。下面我們具體看段代碼,則能很快的理解
使用示例:
首先定義一個業(yè)務(wù)實體類,實體類定義字段、get set方法、構(gòu)造函數(shù)
import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @AllArgsConstructor @NoArgsConstructor /** * 模擬業(yè)務(wù)對象實體類 * @author csdn:孟秋與你 */ public class MyBizEntity { private String name; private Integer age; }
再定義一個事件,事件繼承ApplicationEvent
import org.springframework.context.ApplicationEvent; /** * 自定義事件(繼承ApplicationEvent) * @author csdn:孟秋與你 */ public class MyEvent extends ApplicationEvent { public MyEvent(Object source) { super(source); } public MyEvent() { // java基礎(chǔ):如果父類只有有參構(gòu)造 子類需要使用其它構(gòu)造函數(shù) 必須在構(gòu)造函數(shù)第一行調(diào)用super 因為子類也是調(diào)用父類的構(gòu)造函數(shù) super(null); // do other } }
定義一個監(jiān)聽器,實現(xiàn)ApplicationListener接口:
import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Component; /** * 監(jiān)聽事件 */ @Component public class MyApplicationListener implements ApplicationListener { @Override public void onApplicationEvent(ApplicationEvent applicationEvent) { // 必須判斷自己要的類型 因為會監(jiān)聽到所有繼承ApplicationEvent的事件 if (applicationEvent instanceof MyEvent) { Object source = applicationEvent.getSource(); MyBizEntity bizEntity = (MyBizEntity) source; System.out.println(bizEntity.getName() + ":------------name"); } } }
寫一個接口進(jìn)行測試, 此時監(jiān)聽器就能打印輸出了
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RequestMapping("/test/listener") @RestController public class MainBizController { @Autowired private ApplicationContext context; @GetMapping public String test() { // do something // 模擬要傳遞的業(yè)務(wù)對象 MyBizEntity bizEntity = new MyBizEntity("name",18); MyEvent event = new MyEvent(bizEntity); // 上下文 發(fā)布事件 context.publishEvent(event); return "success"; } }
原理簡析:
為什么發(fā)布了事件,監(jiān)聽器就能夠監(jiān)聽到呢? 其實原理很簡單,就是spring進(jìn)行了一個樸實無華的直接調(diào)用, 我們來看看源碼:
context.publishEvent默認(rèn)是調(diào)用AbstractApplicationContext類的publishEvent方法,而publishEvent方法里面調(diào)用了SimpleApplicationEventMulticaster 類的multicastEvent方法。
tips: 為什么上下文有publishEvent方法 ?
因為ApplicationContext繼承了ApplicationEventPublisher
SimpleApplicationEventMulticaster: 首先會獲取所有實現(xiàn)了ApplicationListener的監(jiān)聽器 (get by type就可以獲取到), 接著會執(zhí)行 invokeListener方法
我們看看最后doInvokeListener 做了什么:
通過上面源碼鏈路,我們不難發(fā)現(xiàn) 其實就是調(diào)用了publishEvent方法后,spring在我們不輕易能看到的地方 去調(diào)用了一下監(jiān)聽器的onApplicationEvent 方法而已,通過源碼我們也可以看到 默認(rèn)是同步調(diào)用(沒有定義taskExecutor時), 本質(zhì)上是一個解耦,把原本可能要寫在一起的業(yè)務(wù)代碼拆分了。
如何異步發(fā)送
通過源碼可以看到 SimpleApplicationEventMulticaster類的multicastEvent方法,會先獲取Executor ,如果有executor 則通過異步方式發(fā)送
所以問題就變成了這個executor如何才能獲取到值。
可以看到該類有一個public的setTaskExecutor方法
所以我們可以直接將該類的實例作為一個bean給spring托管,代碼如下
@Configuration public class EventConfig { @Bean(name = "applicationEventMulticaster") public SimpleApplicationEventMulticaster simpleApplicationEventMulticaster(TaskExecutor taskExecutor) { SimpleApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster(); eventMulticaster.setTaskExecutor(taskExecutor); return eventMulticaster; } }
這里涉及到一個spring的基礎(chǔ)知識,上面代碼的參數(shù)是(TaskExecutor taskExecutor) 所以需要先有一個executor bean,代碼示例如下:
@Configuration public class AsyncInBootConfig { /** 核心線程池大小 */ private int corePoolSize = 20; /** * 最大可創(chuàng)建的線程數(shù) */ private int maxPoolSize = 50; /** * 隊列最大長度 */ private int queueCapacity = 100; /** * 線程池維護(hù)線程所允許的空閑時間 */ private int keepAliveSeconds = 300; @Primary @Bean(name = "threadPool") /** * springboot 線程池方式 */ public ThreadPoolTaskExecutor threadPoolTaskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setMaxPoolSize(maxPoolSize); executor.setCorePoolSize(corePoolSize); executor.setQueueCapacity(queueCapacity); executor.setKeepAliveSeconds(keepAliveSeconds); // 線程池對拒絕任務(wù)(無線程可用)的處理策略 executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); return executor; } }
這樣在發(fā)布消息時,就會使用全局配置的這個threadPool線程池了
到此這篇關(guān)于spring中ApplicationListener的使用小結(jié)的文章就介紹到這了,更多相關(guān)spring ApplicationListener內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
如何解決UnsupportedOperationException異常問題
這篇文章主要介紹了如何解決UnsupportedOperationException異常問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-05-05官方詳解HDFS?Balancer工具主要調(diào)優(yōu)參數(shù)
這篇文章主要為大家介紹了HDFS?Balancer工具主要調(diào)優(yōu)參數(shù)的?官方詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03舉例講解Java的RTTI運(yùn)行時類型識別機(jī)制
這篇文章主要介紹了Java的RTTI運(yùn)行時類型識別機(jī)制,包括泛化的Class引用以及類型檢查instanceof等知識點,需要的朋友可以參考下2016-05-05