《框架封装者 · 自定义初始化事件》
📢 大家好,我是 【战神刘玉栋】,有10多年的研发经验,致力于前后端技术栈的知识沉淀和传播。 💗
🌻 CSDN入驻不久,希望大家多多支持,后续会继续提升文章质量,绝不滥竽充数,如需交流,欢迎留言评论。👍
文章目录
- 写在前面的话
- 构思阶段
- 实现阶段
- 应用阶段
- 总结陈词
写在前面的话
上篇博文介绍了《监听器 Listener》,主要介绍了 Spring 监听器的用法,用的最多的还是初始化监听器,可以程序启动之前执行一些诸如,数据加载到缓存等动作。
显然,这个用法很简单,关于 ContextRefreshedEvent 的初始化监听,作为具体某个模块的开发人员完全可以写一个 Spring 监听器类,完成自己想要的初始化动作,So easy~
但该用法存在一些局限性:
首先,该操作是同步的,若初始化执行的程序逻辑耗时较多,会影响整个服务的启动时长,进而引发一系列问题,例如KS8误判启动失败等;
其次,该操作中,只要业务逻辑存在未把控到位的情况,意外抛出了异常,那么将直接导致程序启动失败,这可能是违背初衷的;
总之,这两点因素带来的影响都很大,那么作为一个框架搭建人员,如何应对这些现象,如何给开发人员更灵活的编码体验,这个是需要我们思考的。
构思阶段
情况大致了解了,开始构思如何处理这个需求呢?
上面提到的两个问题,肯定有人要问了,业务开发人员直接可以通过编码的方式避免。例如添加异步多线程操作,或者将异常catch住,有什么需要框架封装人员好考虑的?
其实不然,并不是所有开发人员都有良好的编程习惯,可以对自己的代码负责。框架封装人员就是要为大部分的开发人员做兜底操作,替他们考虑好可能的问题,让他们更明确自己的代码走向。
那说了怎么多,怎么解决呢?答案就是包装一层,对异步和异常进行可配置,具体往下看实现。
实现阶段
1、框架层面,定义一个初始化类,ApplicationInitializerInvoker,其也是实现 ApplicationListener 接口;
2、定义一个操作接口 ApplicationInitializer,包含是否异步、是否抛出异常等属性;
3、定义一个抽象类 AbstractApplicationInitializer,实现 ApplicationInitializer 接口的若干方法;
4、在第一步的ApplicationInitializerInvoker#onApplicationEvent方法中,获取项目的所有ApplicationInitializer接口的Bean,包含框架内置的和用户自定义的,执行初始化逻辑;
5、至此,流程结束,详见下方代码示例(部分)。
public class ApplicationInitializerInvoker implements ApplicationListener<ApplicationStartedEvent> {private final List<ApplicationInitializer> initializers;@Overridepublic void onApplicationEvent(ApplicationStartedEvent event) {// 是否已经处理过(多个容器加载的情况下事件会被多次刷新)if (this.processed.getAndSet(true)) {return;}if (initializers == null || initializers.isEmpty()) {return;}initializers.sort(Comparator.comparingInt(ApplicationInitializer::getOrderIndex));log.info("[应用初始化事件] 共找到{}个初始化事件,开始初始化...", 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("[应用初始化事件] 初始化完成,总耗时:{}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("应用程序初始化失败:[{}]}", 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("应用程序初始化失败:[{}] - 原因:{}", initializer.getClass().getName(), e.getMessage(), e);}});}
}public interface ApplicationInitializer {/*** 是否异步*/default boolean isAsync() {return false;}/*** 要执行初始化之前的判断逻辑** @param event 容器事件* @return , true为执行 , false 不执行*/default boolean preInit(ApplicationStartedEvent event) {return true;}/*** 执行顺序(值越小优先级越高)** @return 默认优先级为0*/default int getOrderIndex() {return 0;}/*** 事件名,用于描述当前初始化的动作,可以为空*/default String eventName() {return null;}/*** 是否可以抛出异常** @return true = 初始化发生异常会中断程序的启动 , false = 初始化发生异常不会中断程序的启动*/default boolean throwable() {return true;}/*** 初始化逻辑** @param event 容器事件* @throws Exception 初始化时发生的异常*/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("[应用初始化事件] [{}] [{}] 初始化完成,耗时:{}ms", asyncType, eventName, end - start);}/*** 执行初始化** @param event 容器事件* @throws Exception .*/protected abstract void doInit(ApplicationStartedEvent event) throws Exception;
}
应用阶段
开发人员怎么使用呢?
如下所示,只需要继承框架封装的抽象类AbstractApplicationInitializer即可,实现doInit方法即可。
可以按需配置是否异步、是否可以抛出异常等等。
@Slf4j
@Component
public class Portalnitializer extends AbstractApplicationInitializer {@Overrideprotected void doInit(ApplicationStartedEvent event) {log.info("自定义初始化事件");}/*** 是否异步*/@Overridepublic boolean isAsync() {return false;}/*** 是否可以抛出异常* @return true = 初始化发生异常会中断程序的启动 , false = 初始化发生异常不会中断程序的启动*/@Overridepublic boolean throwable() {return false;}
}
总结陈词
上文介绍了框架封装人员,如何提供一个相对灵活的初始化自定义事件,让业务开发人员可以用的更顺手。
本系列博文也以此示例开篇,介绍框架搭建人员如何以恰当的方式应对各式各样的情况,这也是此专栏的主题。
💗 后续将持续更新,请多多支持!
相关文章:
《框架封装者 · 自定义初始化事件》
📢 大家好,我是 【战神刘玉栋】,有10多年的研发经验,致力于前后端技术栈的知识沉淀和传播。 💗 🌻 CSDN入驻不久,希望大家多多支持,后续会继续提升文章质量,绝不滥竽充数…...
ActiViz实战:使用vtkImageClip和vtkImageActor根据滑动条来显示当前图像数据切面
文章目录 一、效果预览二、代码实现三、源码地址一、效果预览 ActiViz实现图像数据切面显示 二、代码实现 public partial class Form1 : Form {private vtkRenderWindowInteractor _interactor;private vtkRenderer _renderer...

【论文笔记】BEVCar: Camera-Radar Fusion for BEV Map and Object Segmentation
原文链接:https://arxiv.org/abs/2403.11761 0. 概述 本文的BEVCar模型是基于环视图像和雷达融合的BEV目标检测和地图分割模型,如图所示。模型的图像分支利用可变形注意力,将图像特征提升到BEV空间中,其中雷达数据用于初始化查询…...

圆通寄15kg30kg一般多少钱?寄大件物品怎么寄最便宜?
作为一名即将毕业的大学生,搬家成了我和室友们共同的难题。尤其是在寄送大件物品时,如何省钱、如何打包、选择哪家快递公司等问题让我们头疼不已。今天,我就来分享一些寄大件物品的省钱技巧以及打包方法,希望对大家有所帮助。 一…...

transformer初探
transformer初探 self-attentionmultihead-attentionencoderdecoder self-attention 其实就是三个矩阵, W q W_q Wq、 W k W_k Wk、 W v W_v Wv,这三个矩阵就是需要训练的参数。分别得到每个token对应的 q q q k k k v v v,其中 q …...

JUC并发编程基础(包含线程概念,状态等具体实现)
一.JUC并发编程基础 1. 并行与并发 1.1 并发: 是在同一实体上的多个事件是在一台处理器上"同时处理多个任务"同一时刻,其实是只有一个事件在发生. 即多个线程抢占同一个资源. 1.2 并行 是在不同实体上的多个事件是在多台处理器上同时处理多个任务同一时刻,大家…...
集中管理和分析日志:使用 ELK 套件构建强大的日志管理平台
集中管理和分析日志:使用 ELK 套件构建强大的日志管理平台 日志是监控和调试应用程序和系统的重要工具。集中管理和分析日志可以帮助你快速定位问题、了解系统运行状况和性能,并提高你的日志管理效率。ELK 是一个流行的日志管理解决方案,由 …...
深度学习 - 模型的保存与部署方式汇总
深度学习模型保存和加载格式科普 在深度学习中,模型的保存和加载是非常重要的环节。不同的格式有不同的特点和适用场景。本文将为新手朋友们介绍几种常见的模型格式,包括它们的简介、保存方式、加载方式、优缺点以及应用场景。 1. PyTorch (.pth, .pt)…...

人工智能对网络安全有何影响?
人工智能网络安全在短期、中期和长期如何变化 当今数字时代网络安全的重要性 在谈论人工智能在网络安全中的作用时,必须首先考虑短期影响,因为它们是最明显的,而且它是一个未知的领域,需要超越直接炒作的能力。 因此࿰…...
Oracle的RECYCLEBIN回收站:轻松恢复误删对象
目录 Oracle的RECYCLEBIN回收站:轻松恢复误删对象一、概念二、工作原理三、使用方法1 查看回收站中的对象2 恢复回收站中的对象2.1 恢复表(TABLE)2.2 恢复索引(INDEX)2.3 恢复视图(VIEW)2.4 恢复…...
Android 内存原理详解以及优化(二)
上一篇讲了内存原理,如果还没看可以先看上一篇:Android 内存原理详解以及优化(一) 这一篇我总结一下我们经常遇到的内存优化问题: 1.内存抖动 自定义view的ondraw是会被频繁调用的,那在这个方法里面就不能频…...
Shell学习——Shell变量
文章目录 Shell变量使用变量只读变量删除变量变量类型字符串变量: 在 Shell中,变量通常被视为字符串。整数变量: 在一些Shell中,你可以使用 declare 或 typeset 命令来声明整数变量。数组变量: Shell 也支持数组&#…...
Java中的持续集成与持续部署(CI/CD)
Java中的持续集成与持续部署(CI/CD) 大家好,我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿!今天我们将深入探讨Java中的持续集成(Co…...

极狐GitLab 将亮相2024空天信息大会暨数字地球生态峰会,携手中科星图赋能空天行业开发者
GitLab 是一个全球知名的一体化 DevOps 平台,很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab :https://gitlab.cn/install?channelcontent&utm_sourcecsdn 是 GitLab 在中国的发行版,专门为中国程序员服务。可以一键式部署…...

Beats:使用 Filebeat 从 Python 应用程序中提取日志
本指南演示了如何从 Python 应用程序中提取日志并将其安全地传送到 Elasticsearch Service 部署中。你将设置 Filebeat 来监控具有标准 Elastic Common Schema (ECS) 格式字段的 JSON 结构日志文件,然后你将在 Kibana 中查看日志事件发生的实时可视化。虽然此示例使…...

51单片机第23步_定时器1工作在模式0(13位定时器)
重点学习51单片机定时器1工作在模式0的应用。 在51单片机中,定时器1工作在模式0,它和定时器0一样,TL1占低5位,TH1占高8位,合计13位,也是向上计数。 1、定时器1工作在模式0 1)、定时器1工作在模式0的框图…...
linux的服务管理
systemd systemd 是一个系统和服务管理器,用于Linux操作系统中,旨在替代传统的Unix系统V初始化系统(SysV init)。 不一定所有使用 yum 安装的软件都可以通过 systemctl start 来管理。能否通过 systemctl start 管理取决于软件包…...

动手学深度学习(Pytorch版)代码实践 -循环神经网络-53语言模型和数据集
53语言模型和数据集 1.自然语言统计 引入库和读取数据: import random import torch from d2l import torch as d2l import liliPytorch as lp import numpy as np import matplotlib.pyplot as plttokens lp.tokenize(lp.read_time_machine())一元语法…...
Python 学习之自动化运维技术(八)
Python 的自动化运维技术 Python的自动化运维技术是指利用Python编程语言和相关工具实现运维工作的自动化,以提高效率、减轻工作负担。以下是对Python自动化运维技术的清晰归纳和详细介绍: 一、自动化运维的核心优势 ● 提高效率:通过自动化脚…...

【python】PyQt5可视化开发,如何设计鼠标显示的形状?
✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,…...

19c补丁后oracle属主变化,导致不能识别磁盘组
补丁后服务器重启,数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后,存在与用户组权限相关的问题。具体表现为,Oracle 实例的运行用户(oracle)和集…...

汽车生产虚拟实训中的技能提升与生产优化
在制造业蓬勃发展的大背景下,虚拟教学实训宛如一颗璀璨的新星,正发挥着不可或缺且日益凸显的关键作用,源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例,汽车生产线上各类…...
电脑插入多块移动硬盘后经常出现卡顿和蓝屏
当电脑在插入多块移动硬盘后频繁出现卡顿和蓝屏问题时,可能涉及硬件资源冲突、驱动兼容性、供电不足或系统设置等多方面原因。以下是逐步排查和解决方案: 1. 检查电源供电问题 问题原因:多块移动硬盘同时运行可能导致USB接口供电不足&#x…...

uniapp微信小程序视频实时流+pc端预览方案
方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度WebSocket图片帧定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐RTMP推流TRTC/即构SDK推流❌ 付费方案 (部分有免费额度&#x…...
【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)
要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况,可以通过以下几种方式模拟或触发: 1. 增加CPU负载 运行大量计算密集型任务,例如: 使用多线程循环执行复杂计算(如数学运算、加密解密等)。运行图…...
Matlab | matlab常用命令总结
常用命令 一、 基础操作与环境二、 矩阵与数组操作(核心)三、 绘图与可视化四、 编程与控制流五、 符号计算 (Symbolic Math Toolbox)六、 文件与数据 I/O七、 常用函数类别重要提示这是一份 MATLAB 常用命令和功能的总结,涵盖了基础操作、矩阵运算、绘图、编程和文件处理等…...

k8s业务程序联调工具-KtConnect
概述 原理 工具作用是建立了一个从本地到集群的单向VPN,根据VPN原理,打通两个内网必然需要借助一个公共中继节点,ktconnect工具巧妙的利用k8s原生的portforward能力,简化了建立连接的过程,apiserver间接起到了中继节…...

全志A40i android7.1 调试信息打印串口由uart0改为uart3
一,概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本:2014.07; Kernel版本:Linux-3.10; 二,Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01),并让boo…...
IP如何挑?2025年海外专线IP如何购买?
你花了时间和预算买了IP,结果IP质量不佳,项目效率低下不说,还可能带来莫名的网络问题,是不是太闹心了?尤其是在面对海外专线IP时,到底怎么才能买到适合自己的呢?所以,挑IP绝对是个技…...
Python 训练营打卡 Day 47
注意力热力图可视化 在day 46代码的基础上,对比不同卷积层热力图可视化的结果 import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import matplotlib.pypl…...