javascript
今天,我要教妹子学会Spring:Aware、异步编程、计划任务
來源 | 沉默王二
教妹子學(xué) Spring,沒見過這么放肆的標(biāo)題吧?
作者我有一個(gè)漂亮如花的妹妹(見封面圖,別問我怎么又變了?還不能一天做個(gè)夢(mèng)了?),她叫什么呢?我想聰明的讀者能猜得出:沉默王三,沒錯(cuò),年方三六。父母正考慮讓她向我學(xué)習(xí),做一名正兒八經(jīng)的 Java 程序員。我一開始是反對(duì)的,因?yàn)槌绦騿T這行業(yè)容易掉頭發(fā),女生可不適合掉頭發(fā)。但家命難為啊,與其反對(duì),不如做點(diǎn)更積極的事情,比如說寫點(diǎn)有趣的文章教教她。
“二哥,聽說今天要學(xué)習(xí) Spring 的 Aware、異步編程、計(jì)劃任務(wù),真的是翹首以盼啊。”
Spring Aware
“二哥,據(jù)說 Aware 的目的是讓 Bean 獲取 Spring 容器的服務(wù),你能給我具體說說嗎?”
“沒問題啊。”
Bean 一般不需要了解容器的狀態(tài)和直接使用容器,但是在某些情況下,需要在 Bean 中直接對(duì)容器進(jìn)行操作,這時(shí)候,就可以通過特定的 Aware 接口來完成。常見的 Spring Aware 接口有下面這些:
1)BeanNameAware
新建一個(gè) MyBeanName 類,內(nèi)容如下:
public?class?MyBeanName?implements?BeanNameAware?{@Overridepublic?void?setBeanName(String?beanName)?{System.out.println(beanName);} }MyBeanName 實(shí)現(xiàn)了 BeanNameAware 接口,并重寫了?setBeanName()?方法。beanName 參數(shù)表示 Bean 在 Spring 容器中注冊(cè)的 name。
新建一個(gè) Config 類,內(nèi)容如下:
@Configuration public?class?Config?{@Bean(name?=?"myCustomBeanName")public?MyBeanName?getMyBeanName()?{return?new?MyBeanName();} }@Bean?注解用在?getMyBeanName()?方法上,表明當(dāng)前方法返回一個(gè) Bean 對(duì)象(MyBeanName),并通過 name 屬性指定 Bean 的名字為“myCustomBeanName”。
新建 BeanNameMain 類,代碼如下:
public?class?BeanNameMain?{public?static?void?main(String[]?args)?{AnnotationConfigApplicationContext?context?=?new?AnnotationConfigApplicationContext(BeanNameConfig.class);MyBeanName?myBeanName?=?context.getBean(MyBeanName.class);context.close();} }程序輸出的結(jié)果如下所示: myCustomBeanName如果把?@Bean()?注解中的?(name = "myCustomBeanName)"?去掉的話,程序輸出的內(nèi)容將會(huì)是 BeanNameConfig 類的?getMyBeanName()?的方法名“getMyBeanName”。
2)BeanFactoryAware
新建一個(gè) MyBeanFactory 類,內(nèi)容如下:
public?class?MyBeanFactory?implements?BeanFactoryAware?{private?BeanFactory?beanFactory;@Overridepublic?void?setBeanFactory(BeanFactory?beanFactory)?throws?BeansException?{this.beanFactory?=?beanFactory;}public?void?getMyBeanName()?{MyBeanName?myBeanName?=?beanFactory.getBean(MyBeanName.class);System.out.println(beanFactory.isSingleton("myCustomBeanName"));System.out.println(beanFactory.isSingleton("getMyBeanFactory"));} }借助?setBeanFactory()?方法,可以將容器中的 BeanFactory 賦值給 MyBeanFactory 類的成員變量 beanFactory,這樣就可以在?getMyBeanName()方法中使用 BeanFactory 了。
通過?getBean()?方法可以獲取 Bean 的實(shí)例;通過?isSingleton()?方法判斷 Bean 是否為一個(gè)單例。
在 Config 類中追加 MyBeanFactory 的 Bean:
@Configuration public?class?Config?{@Bean(name?=?"myCustomBeanName")public?MyBeanName?getMyBeanName()?{return?new?MyBeanName();}@Beanpublic?MyBeanFactory?getMyBeanFactory()?{return?new?MyBeanFactory();} }新建 BeanFactoryMain 類,代碼如下:
public?class?BeanFactoryMain?{public?static?void?main(String[]?args)?{AnnotationConfigApplicationContext?context?=?new?AnnotationConfigApplicationContext(Config.class);MyBeanFactory?myBeanFactory?=?context.getBean(MyBeanFactory.class);myBeanFactory.getMyBeanName();context.close();} }初始化 MyBeanFactory 后就可以調(diào)用?getMyBeanName()?方法了,程序輸出的結(jié)果如下所示:
myCustomBeanName true true結(jié)果符合我們的預(yù)期:MyBeanName 的名字為“myCustomBeanName”,MyBeanName 和 MyBeanFactory 的 scope 都是 singleton。
3)其他幾個(gè) Aware 接口就不再舉例說明了。通常情況下,不要實(shí)現(xiàn) Aware 接口,因?yàn)樗鼤?huì)使 Bean 和 Spring 框架耦合。
異步編程
“二哥,據(jù)說 Spring 可以通過 @Async 來實(shí)現(xiàn)異步編程,你能給我詳細(xì)說說嗎?”
“沒問題啊。”
新建一個(gè) AsyncService 類,內(nèi)容如下:
public?class?AsyncService?{@Asyncpublic?void?execute()?{System.out.println(Thread.currentThread().getName());} }@Async?注解用在 public 方法上,表明?execute()?方法是一個(gè)異步方法。
新建一個(gè) AsyncConfig 類,內(nèi)容如下:
@Configuration @EnableAsync public?class?AsyncConfig?{@Beanpublic?AsyncService?getAsyncService()?{return?new?AsyncService();} }在配置類上使用?@EnableAsync?注解用以開啟異步編程,否則?@Async?注解不會(huì)起作用。
新建一個(gè) AsyncMain 類,內(nèi)容如下:
public?class?AsyncMain?{public?static?void?main(String[]?args)?{AnnotationConfigApplicationContext?context?=?new?AnnotationConfigApplicationContext(AsyncConfig.class);AsyncService?service?=?context.getBean(AsyncService.class);for?(int?i?=?0;?i?<?10;?i++)?{service.execute();}}程序輸出結(jié)果如下:
SimpleAsyncTaskExecutor-1 SimpleAsyncTaskExecutor-9 SimpleAsyncTaskExecutor-7 SimpleAsyncTaskExecutor-8 SimpleAsyncTaskExecutor-10 SimpleAsyncTaskExecutor-3 SimpleAsyncTaskExecutor-2 SimpleAsyncTaskExecutor-4 SimpleAsyncTaskExecutor-6 SimpleAsyncTaskExecutor-5OK,結(jié)果符合我們的預(yù)期,異步編程實(shí)現(xiàn)了。就像你看到的那樣,Spring 提供了一個(gè)默認(rèn)的 SimpleAsyncTaskExecutor 用來執(zhí)行線程,我們也可以在方法級(jí)別和應(yīng)用級(jí)別上對(duì)執(zhí)行器進(jìn)行配置。
1)方法級(jí)別
新建 AsyncConfig 類,內(nèi)容如下:
@Configuration @EnableAsync public?class?AsyncConfig?{@Beanpublic?AsyncService?getAsyncService()?{return?new?AsyncService();}@Bean(name?=?"threadPoolTaskExecutor")public?Executor?threadPoolTaskExecutor()?{ThreadPoolTaskExecutor?executor?=?new?ThreadPoolTaskExecutor();executor.setCorePoolSize(5);return?executor;} }在配置類中創(chuàng)建了一個(gè)返回類型為 Executor 的 Bean,其名稱定義為“threadPoolTaskExecutor”,并且重新設(shè)置了 ThreadPoolTaskExecutor 的核心線程池大小,默認(rèn)為 1,現(xiàn)在修改為 5。
新進(jìn) AsyncService 類,內(nèi)容如下:
public?class?AsyncService?{@Async("threadPoolTaskExecutor")public?void?execute()?{System.out.println(Thread.currentThread().getName());} }@Async?注解上需要指定我們之前配置的線程池執(zhí)行器“threadPoolTaskExecutor”。
新建 AsyncMain 類,內(nèi)容如下:
public?class?AsyncMain?{public?static?void?main(String[]?args)?{AnnotationConfigApplicationContext?context?=?new?AnnotationConfigApplicationContext(AsyncConfig.class);AsyncService?service?=?context.getBean(AsyncService.class);for?(int?i?=?0;?i?<?10;?i++)?{service.execute();}} }程序運(yùn)行結(jié)果如下:
threadPoolTaskExecutor-1 threadPoolTaskExecutor-2 threadPoolTaskExecutor-4 threadPoolTaskExecutor-3 threadPoolTaskExecutor-5 threadPoolTaskExecutor-3 threadPoolTaskExecutor-2 threadPoolTaskExecutor-4 threadPoolTaskExecutor-1 threadPoolTaskExecutor-5從結(jié)果中可以看得出,線程池執(zhí)行器變成了“threadPoolTaskExecutor”,并且大小為 5。
2)應(yīng)用級(jí)別新建 AsyncConfig 類,內(nèi)容如下:
@Configuration @EnableAsync public?class?AsyncConfig?implements?AsyncConfigurer?{@Beanpublic?AsyncService?getAsyncService()?{return?new?AsyncService();}@Overridepublic?Executor?getAsyncExecutor()?{ThreadPoolTaskExecutor?executor?=?new?ThreadPoolTaskExecutor();executor.setCorePoolSize(3);executor.initialize();return?executor;} }需要實(shí)現(xiàn) AsyncConfigurer 接口,并重寫?getAsyncExecutor()?方法,這次設(shè)置線程池的大小為 3。注意執(zhí)行器要執(zhí)行一次?initialize()?方法。新進(jìn) AsyncService 類,內(nèi)容如下:
public?class?AsyncService?{@Asyncpublic?void?execute()?{System.out.println(Thread.currentThread().getName());} }新建 AsyncMain 類,內(nèi)容如下:
public?class?AsyncMain?{public?static?void?main(String[]?args)?{AnnotationConfigApplicationContext?context?=?new?AnnotationConfigApplicationContext(AsyncConfig.class);AsyncService?service?=?context.getBean(AsyncService.class);for?(int?i?=?0;?i?<?10;?i++)?{service.execute();}} }程序運(yùn)行結(jié)果如下:
ThreadPoolTaskExecutor-2 ThreadPoolTaskExecutor-2 ThreadPoolTaskExecutor-2 ThreadPoolTaskExecutor-2 ThreadPoolTaskExecutor-2 ThreadPoolTaskExecutor-2 ThreadPoolTaskExecutor-2 ThreadPoolTaskExecutor-2 ThreadPoolTaskExecutor-1 ThreadPoolTaskExecutor-3從結(jié)果中可以看得出,線程池執(zhí)行器變成了“ThreadPoolTaskExecutor”,并且大小為 3。
計(jì)劃任務(wù)
“二哥,據(jù)說 Spring 可以通過 @Scheduled 來實(shí)現(xiàn)計(jì)劃任務(wù),你能給我詳細(xì)說說怎么實(shí)現(xiàn)嗎?”
“沒問題啊。”
新建一個(gè) ScheduledService 類,內(nèi)容如下:
@Service public?class?ScheduledService?{@Scheduled(fixedDelay?=?1000)public?void?scheduleFixedDelayTask()?{System.out.println("固定時(shí)間段后執(zhí)行任務(wù)?-?"?+?System.currentTimeMillis()?/?1000);}@Scheduled(fixedRate?=?1000)public?void?scheduleFixedRateTask()?{System.out.println("固定的頻率執(zhí)行任務(wù)?-?"?+?System.currentTimeMillis()?/?1000);}@Scheduled(cron?=?"0/2?*?*?*?*??")public?void?scheduleTaskUsingCronExpression()?{long?now?=?System.currentTimeMillis()?/?1000;System.out.println("Cron?表達(dá)式執(zhí)行任務(wù)?-?"?+?now);} }@Service?注解用于指定 ScheduledService 類為一個(gè)業(yè)務(wù)層的 Bean。@Scheduled?注解用于指定當(dāng)前方法(返回類型為 void,無參)為一個(gè)任務(wù)執(zhí)行方法,常見的用法有以下 3 種:
1)fixedDelay 用于確保任務(wù)執(zhí)行的完成時(shí)間與任務(wù)下一次執(zhí)行的開始時(shí)間之間存在 n 毫秒的延遲,下一次任務(wù)執(zhí)行前,上一次任務(wù)必須執(zhí)行完。
2)fixedRate 用于確保每 n 毫秒執(zhí)行一次計(jì)劃任務(wù),即使最后一次調(diào)用可能仍在運(yùn)行。
3)Cron 表達(dá)式比 fixedDelay 和 fixedRate 都要靈活,由 7 個(gè)部分組成,各部分之間用空格隔開,其完整的格式如下所示:
Seconds?Minutes?Hours?Day-of-Month?Month?Day-of-Week?Year單詞都很簡單,就不用我翻譯了。其中 Year 是可選項(xiàng)。常見的范例如下所示:
*/5?*?*?*?*????每隔?5?秒執(zhí)行一次 0?*/1?*?*?*????每隔?1?分鐘執(zhí)行一次 0?0?23?*?*????每天?23?點(diǎn)執(zhí)行一次 0?0?1?*?*?? ?每天凌晨 1 點(diǎn)執(zhí)行一次: 0?0?1?1?*????每月?1?號(hào)凌晨?1?點(diǎn)執(zhí)行一次 0?0?23?L?*????每月最后一天?23?點(diǎn)執(zhí)行一次 0?0?1???*?L??每周星期天凌晨?1?點(diǎn)執(zhí)行一次 0?26,29,33?*?*?*????在?26?分、29?分、33?分執(zhí)行一次 0?0?0,13,18,21?*?*???每天的?0?點(diǎn)、13?點(diǎn)、18?點(diǎn)、21?點(diǎn)各執(zhí)行一次新建 ScheduledConfig 類,內(nèi)容如下:
@Configuration @EnableScheduling @ComponentScan("high.scheduled") public?class?ScheduledConfig?{ }@EnableScheduling?注解用于開啟計(jì)劃任務(wù)。@ComponentScan?注解用于掃描當(dāng)前包下的類,如果它使用了注解(比如?@Service),就將其注冊(cè)成為一個(gè) Bean。新建 ScheduledMain 類,內(nèi)容如下:
public?class?ScheduledMain?{public?static?void?main(String[]?args)?{AnnotationConfigApplicationContext?context?=?new?AnnotationConfigApplicationContext(ScheduledConfig.class);} }程序運(yùn)行結(jié)果如下:
固定的頻率執(zhí)行任務(wù)?-?1584666273 固定時(shí)間段后執(zhí)行任務(wù)?-?1584666273 Cron?表達(dá)式執(zhí)行任務(wù)?-?1584666274 固定的頻率執(zhí)行任務(wù)?-?1584666274 固定時(shí)間段后執(zhí)行任務(wù)?-?1584666274 固定的頻率執(zhí)行任務(wù)?-?1584666275 固定時(shí)間段后執(zhí)行任務(wù)?-?1584666275 Cron?表達(dá)式執(zhí)行任務(wù)?-?1584666276從結(jié)果中可以看得出,如果任務(wù)之間沒有沖突的話,fixedDelay 任務(wù)之間的間隔和 fixedRate 任務(wù)之間的間隔是相同的,都是 1 秒;Cron 表達(dá)式任務(wù)與上一次任務(wù)之間的間隔為 2 秒。
“二哥,這篇文章中的示例代碼你上傳到 GitHub 了嗎?”
“你到挺貼心啊,三妹。傳送門~”
https://github.com/qinggee/SpringDemo
“二哥,你教得真不錯(cuò),我完全學(xué)會(huì)了,一點(diǎn)也不枯燥。”
“那必須得啊,就是不知道看這篇文章的他們學(xué)會(huì)了沒?”
同時(shí),歡迎所有開發(fā)者掃描下方二維碼填寫《開發(fā)者與AI大調(diào)研》,只需2分鐘,便可收獲價(jià)值299元的「AI開發(fā)者萬人大會(huì)」在線直播門票!
推薦閱讀:在容器上構(gòu)建持續(xù)部署及最佳實(shí)踐初探在Kubernetes上部署一個(gè)簡單的、類PaaS的平臺(tái),原來這么容易!全球呼吸機(jī)告急!醫(yī)療科技巨頭美敦力“開源”設(shè)計(jì)圖和源代碼互聯(lián)網(wǎng)之父確診新冠,一代傳奇:任谷歌副總裁、NASA 訪問科學(xué)家微軟為一人收購一公司?破解索尼程序、寫黑客小說,看他彪悍的程序人生!2020年,這20個(gè)大家都認(rèn)識(shí)的加密交易所過得怎么樣?真香,朕在看了!總結(jié)
以上是生活随笔為你收集整理的今天,我要教妹子学会Spring:Aware、异步编程、计划任务的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 甲骨文Java 14来啦!
- 下一篇: 关于Spring AOP,除了动态代理、