做app網(wǎng)站有哪些功能怎么做ppt
📢 大家好,我是 【戰(zhàn)神劉玉棟】,有10多年的研發(fā)經(jīng)驗,致力于前后端技術(shù)棧的知識沉淀和傳播。 💗
🌻 CSDN入駐不久,希望大家多多支持,后續(xù)會繼續(xù)提升文章質(zhì)量,絕不濫竽充數(shù),如需交流,歡迎留言評論。👍
文章目錄
- 寫在前面的話
- 構(gòu)思階段
- 實現(xiàn)階段
- 應(yīng)用階段
- 總結(jié)陳詞
寫在前面的話
上篇博文介紹了《監(jiān)聽器 Listener》,主要介紹了 Spring 監(jiān)聽器的用法,用的最多的還是初始化監(jiān)聽器,可以程序啟動之前執(zhí)行一些諸如,數(shù)據(jù)加載到緩存等動作。
顯然,這個用法很簡單,關(guān)于 ContextRefreshedEvent 的初始化監(jiān)聽,作為具體某個模塊的開發(fā)人員完全可以寫一個 Spring 監(jiān)聽器類,完成自己想要的初始化動作,So easy~
但該用法存在一些局限性:
首先,該操作是同步的,若初始化執(zhí)行的程序邏輯耗時較多,會影響整個服務(wù)的啟動時長,進而引發(fā)一系列問題,例如KS8誤判啟動失敗等;
其次,該操作中,只要業(yè)務(wù)邏輯存在未把控到位的情況,意外拋出了異常,那么將直接導(dǎo)致程序啟動失敗,這可能是違背初衷的;
總之,這兩點因素帶來的影響都很大,那么作為一個框架搭建人員,如何應(yīng)對這些現(xiàn)象,如何給開發(fā)人員更靈活的編碼體驗,這個是需要我們思考的。
構(gòu)思階段
情況大致了解了,開始構(gòu)思如何處理這個需求呢?
上面提到的兩個問題,肯定有人要問了,業(yè)務(wù)開發(fā)人員直接可以通過編碼的方式避免。例如添加異步多線程操作,或者將異常catch住,有什么需要框架封裝人員好考慮的?
其實不然,并不是所有開發(fā)人員都有良好的編程習(xí)慣,可以對自己的代碼負責(zé)??蚣芊庋b人員就是要為大部分的開發(fā)人員做兜底操作,替他們考慮好可能的問題,讓他們更明確自己的代碼走向。
那說了怎么多,怎么解決呢?答案就是包裝一層,對異步和異常進行可配置,具體往下看實現(xiàn)。
實現(xiàn)階段
1、框架層面,定義一個初始化類,ApplicationInitializerInvoker,其也是實現(xiàn) ApplicationListener 接口;
2、定義一個操作接口 ApplicationInitializer,包含是否異步、是否拋出異常等屬性;
3、定義一個抽象類 AbstractApplicationInitializer,實現(xiàn) ApplicationInitializer 接口的若干方法;
4、在第一步的ApplicationInitializerInvoker#onApplicationEvent方法中,獲取項目的所有ApplicationInitializer接口的Bean,包含框架內(nèi)置的和用戶自定義的,執(zhí)行初始化邏輯;
5、至此,流程結(jié)束,詳見下方代碼示例(部分)。
public class ApplicationInitializerInvoker implements ApplicationListener<ApplicationStartedEvent> {private final List<ApplicationInitializer> initializers;@Overridepublic void onApplicationEvent(ApplicationStartedEvent event) {// 是否已經(jīng)處理過(多個容器加載的情況下事件會被多次刷新)if (this.processed.getAndSet(true)) {return;}if (initializers == null || initializers.isEmpty()) {return;}initializers.sort(Comparator.comparingInt(ApplicationInitializer::getOrderIndex));log.info("[應(yīng)用初始化事件] 共找到{}個初始化事件,開始初始化...", initializers.size());long start = System.currentTimeMillis();OnelinkInitResult onelinkInitResult = new OnelinkInitResult();for (ApplicationInitializer initializer : initializers) {// 異步初始化if (initializer.isAsync()) {this.initWithAsync(initializer, event, onelinkInitResult);} else {this.init(initializer, event, onelinkInitResult);}}long end = System.currentTimeMillis();log.info("[應(yīng)用初始化事件] 初始化完成,總耗時:{}ms", end - start);}/*** 同步方式初始化*/private void init(ApplicationInitializer initializer, ApplicationStartedEvent event, OnelinkInitResult initResult) throws Exception {if (!initializer.preInit(event)) {return;}try {initializer.init(event);this.finish(initializer, initResult);} catch (Exception e) {initResult.addFailureEx(e);if (initializer.throwable()) {throw e;} else {log.error("應(yīng)用程序初始化失敗:[{}]}", initializer.getClass().getName(), e);}}}/*** 異步方式初始化*/private void initWithAsync(ApplicationInitializer initializer, ApplicationStartedEvent event, OnelinkInitResult initResult) {ThreadUtil.execute(() -> {try {this.init(initializer, event, initResult);this.finish(initializer, initResult);} catch (Exception e) {initResult.addFailureEx(e);log.error("應(yīng)用程序初始化失敗:[{}] - 原因:{}", initializer.getClass().getName(), e.getMessage(), e);}});}
}public interface ApplicationInitializer {/*** 是否異步*/default boolean isAsync() {return false;}/*** 要執(zhí)行初始化之前的判斷邏輯** @param event 容器事件* @return , true為執(zhí)行 , false 不執(zhí)行*/default boolean preInit(ApplicationStartedEvent event) {return true;}/*** 執(zhí)行順序(值越小優(yōu)先級越高)** @return 默認優(yōu)先級為0*/default int getOrderIndex() {return 0;}/*** 事件名,用于描述當(dāng)前初始化的動作,可以為空*/default String eventName() {return null;}/*** 是否可以拋出異常** @return true = 初始化發(fā)生異常會中斷程序的啟動 , false = 初始化發(fā)生異常不會中斷程序的啟動*/default boolean throwable() {return true;}/*** 初始化邏輯** @param event 容器事件* @throws Exception 初始化時發(fā)生的異常*/void init(ApplicationStartedEvent event) throws Exception;}public abstract class AbstractApplicationInitializer implements ApplicationInitializer {@Overridepublic void init(ApplicationStartedEvent event) throws Exception {long start = System.currentTimeMillis();String eventName = this.eventName() == null ? this.getClass().getName() : this.eventName();String asyncType = this.isAsync() ? "異步方式" : "同步方式";if (preInit(event)) {this.doInit(event);}long end = System.currentTimeMillis();log.info("[應(yīng)用初始化事件] [{}] [{}] 初始化完成,耗時:{}ms", asyncType, eventName, end - start);}/*** 執(zhí)行初始化** @param event 容器事件* @throws Exception .*/protected abstract void doInit(ApplicationStartedEvent event) throws Exception;
}
應(yīng)用階段
開發(fā)人員怎么使用呢?
如下所示,只需要繼承框架封裝的抽象類AbstractApplicationInitializer即可,實現(xiàn)doInit方法即可。
可以按需配置是否異步、是否可以拋出異常等等。
@Slf4j
@Component
public class Portalnitializer extends AbstractApplicationInitializer {@Overrideprotected void doInit(ApplicationStartedEvent event) {log.info("自定義初始化事件");}/*** 是否異步*/@Overridepublic boolean isAsync() {return false;}/*** 是否可以拋出異常* @return true = 初始化發(fā)生異常會中斷程序的啟動 , false = 初始化發(fā)生異常不會中斷程序的啟動*/@Overridepublic boolean throwable() {return false;}
}
總結(jié)陳詞
上文介紹了框架封裝人員,如何提供一個相對靈活的初始化自定義事件,讓業(yè)務(wù)開發(fā)人員可以用的更順手。
本系列博文也以此示例開篇,介紹框架搭建人員如何以恰當(dāng)?shù)姆绞綉?yīng)對各式各樣的情況,這也是此專欄的主題。
💗 后續(xù)將持續(xù)更新,請多多支持!