当前位置: 首页 > news >正文

《框架封装者 · 自定义初始化事件》

📢 大家好,我是 【战神刘玉栋】,有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;}
}

总结陈词

上文介绍了框架封装人员,如何提供一个相对灵活的初始化自定义事件,让业务开发人员可以用的更顺手。
本系列博文也以此示例开篇,介绍框架搭建人员如何以恰当的方式应对各式各样的情况,这也是此专栏的主题。
💗 后续将持续更新,请多多支持!

相关文章:

《框架封装者 · 自定义初始化事件》

&#x1f4e2; 大家好&#xff0c;我是 【战神刘玉栋】&#xff0c;有10多年的研发经验&#xff0c;致力于前后端技术栈的知识沉淀和传播。 &#x1f497; &#x1f33b; CSDN入驻不久&#xff0c;希望大家多多支持&#xff0c;后续会继续提升文章质量&#xff0c;绝不滥竽充数…...

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

原文链接&#xff1a;https://arxiv.org/abs/2403.11761 0. 概述 本文的BEVCar模型是基于环视图像和雷达融合的BEV目标检测和地图分割模型&#xff0c;如图所示。模型的图像分支利用可变形注意力&#xff0c;将图像特征提升到BEV空间中&#xff0c;其中雷达数据用于初始化查询…...

圆通寄15kg30kg一般多少钱?寄大件物品怎么寄最便宜?

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

transformer初探

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

JUC并发编程基础(包含线程概念,状态等具体实现)

一.JUC并发编程基础 1. 并行与并发 1.1 并发: 是在同一实体上的多个事件是在一台处理器上"同时处理多个任务"同一时刻,其实是只有一个事件在发生. 即多个线程抢占同一个资源. 1.2 并行 是在不同实体上的多个事件是在多台处理器上同时处理多个任务同一时刻,大家…...

集中管理和分析日志:使用 ELK 套件构建强大的日志管理平台

集中管理和分析日志&#xff1a;使用 ELK 套件构建强大的日志管理平台 日志是监控和调试应用程序和系统的重要工具。集中管理和分析日志可以帮助你快速定位问题、了解系统运行状况和性能&#xff0c;并提高你的日志管理效率。ELK 是一个流行的日志管理解决方案&#xff0c;由 …...

深度学习 - 模型的保存与部署方式汇总

深度学习模型保存和加载格式科普 在深度学习中&#xff0c;模型的保存和加载是非常重要的环节。不同的格式有不同的特点和适用场景。本文将为新手朋友们介绍几种常见的模型格式&#xff0c;包括它们的简介、保存方式、加载方式、优缺点以及应用场景。 1. PyTorch (.pth, .pt)…...

人工智能对网络安全有何影响?

人工智能网络安全在短期、中期和长期如何变化 当今数字时代网络安全的重要性 在谈论人工智能在网络安全中的作用时&#xff0c;必须首先考虑短期影响&#xff0c;因为它们是最明显的&#xff0c;而且它是一个未知的领域&#xff0c;需要超越直接炒作的能力。 因此&#xff0…...

Oracle的RECYCLEBIN回收站:轻松恢复误删对象

目录 Oracle的RECYCLEBIN回收站&#xff1a;轻松恢复误删对象一、概念二、工作原理三、使用方法1 查看回收站中的对象2 恢复回收站中的对象2.1 恢复表&#xff08;TABLE&#xff09;2.2 恢复索引&#xff08;INDEX&#xff09;2.3 恢复视图&#xff08;VIEW&#xff09;2.4 恢复…...

Android 内存原理详解以及优化(二)

上一篇讲了内存原理&#xff0c;如果还没看可以先看上一篇&#xff1a;Android 内存原理详解以及优化&#xff08;一&#xff09; 这一篇我总结一下我们经常遇到的内存优化问题&#xff1a; 1.内存抖动 自定义view的ondraw是会被频繁调用的&#xff0c;那在这个方法里面就不能频…...

Shell学习——Shell变量

文章目录 Shell变量使用变量只读变量删除变量变量类型字符串变量&#xff1a; 在 Shell中&#xff0c;变量通常被视为字符串。整数变量&#xff1a; 在一些Shell中&#xff0c;你可以使用 declare 或 typeset 命令来声明整数变量。数组变量&#xff1a; Shell 也支持数组&#…...

Java中的持续集成与持续部署(CI/CD)

Java中的持续集成与持续部署&#xff08;CI/CD&#xff09; 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01;今天我们将深入探讨Java中的持续集成&#xff08;Co…...

极狐GitLab 将亮相2024空天信息大会暨数字地球生态峰会,携手中科星图赋能空天行业开发者

GitLab 是一个全球知名的一体化 DevOps 平台&#xff0c;很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab &#xff1a;https://gitlab.cn/install?channelcontent&utm_sourcecsdn 是 GitLab 在中国的发行版&#xff0c;专门为中国程序员服务。可以一键式部署…...

Beats:使用 Filebeat 从 Python 应用程序中提取日志

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

51单片机第23步_定时器1工作在模式0(13位定时器)

重点学习51单片机定时器1工作在模式0的应用。 在51单片机中&#xff0c;定时器1工作在模式0&#xff0c;它和定时器0一样&#xff0c;TL1占低5位&#xff0c;TH1占高8位&#xff0c;合计13位&#xff0c;也是向上计数。 1、定时器1工作在模式0 1)、定时器1工作在模式0的框图…...

linux的服务管理

systemd systemd 是一个系统和服务管理器&#xff0c;用于Linux操作系统中&#xff0c;旨在替代传统的Unix系统V初始化系统&#xff08;SysV init&#xff09;。 不一定所有使用 yum 安装的软件都可以通过 systemctl start 来管理。能否通过 systemctl start 管理取决于软件包…...

动手学深度学习(Pytorch版)代码实践 -循环神经网络-53语言模型和数据集

53语言模型和数据集 1.自然语言统计 引入库和读取数据&#xff1a; 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())一元语法&#xf…...

Python 学习之自动化运维技术(八)

Python 的自动化运维技术 Python的自动化运维技术是指利用Python编程语言和相关工具实现运维工作的自动化&#xff0c;以提高效率、减轻工作负担。以下是对Python自动化运维技术的清晰归纳和详细介绍&#xff1a; 一、自动化运维的核心优势 ● 提高效率&#xff1a;通过自动化脚…...

【python】PyQt5可视化开发,如何设计鼠标显示的形状?

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…...

五年级数学知识边界总结思考-下册

目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解&#xff1a;由来、作用与意义**一、知识点核心内容****二、知识点的由来&#xff1a;从生活实践到数学抽象****三、知识的作用&#xff1a;解决实际问题的工具****四、学习的意义&#xff1a;培养核心素养…...

Unit 1 深度强化学习简介

Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库&#xff0c;例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体&#xff0c;比如 SnowballFight、Huggy the Do…...

服务器--宝塔命令

一、宝塔面板安装命令 ⚠️ 必须使用 root 用户 或 sudo 权限执行&#xff01; sudo su - 1. CentOS 系统&#xff1a; yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh2. Ubuntu / Debian 系统…...

2025季度云服务器排行榜

在全球云服务器市场&#xff0c;各厂商的排名和地位并非一成不变&#xff0c;而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势&#xff0c;对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析&#xff1a; 一、全球“三巨头”…...

SiFli 52把Imagie图片,Font字体资源放在指定位置,编译成指定img.bin和font.bin的问题

分区配置 (ptab.json) img 属性介绍&#xff1a; img 属性指定分区存放的 image 名称&#xff0c;指定的 image 名称必须是当前工程生成的 binary 。 如果 binary 有多个文件&#xff0c;则以 proj_name:binary_name 格式指定文件名&#xff0c; proj_name 为工程 名&…...

R 语言科研绘图第 55 期 --- 网络图-聚类

在发表科研论文的过程中&#xff0c;科研绘图是必不可少的&#xff0c;一张好看的图形会是文章很大的加分项。 为了便于使用&#xff0c;本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中&#xff0c;获取方式&#xff1a; R 语言科研绘图模板 --- sciRplothttps://mp.…...

认识CMake并使用CMake构建自己的第一个项目

1.CMake的作用和优势 跨平台支持&#xff1a;CMake支持多种操作系统和编译器&#xff0c;使用同一份构建配置可以在不同的环境中使用 简化配置&#xff1a;通过CMakeLists.txt文件&#xff0c;用户可以定义项目结构、依赖项、编译选项等&#xff0c;无需手动编写复杂的构建脚本…...

【PX4飞控】mavros gps相关话题分析,经纬度海拔获取方法,卫星数锁定状态获取方法

使用 ROS1-Noetic 和 mavros v1.20.1&#xff0c; 携带经纬度海拔的话题主要有三个&#xff1a; /mavros/global_position/raw/fix/mavros/gpsstatus/gps1/raw/mavros/global_position/global 查看 mavros 源码&#xff0c;来分析他们的发布过程。发现前两个话题都对应了同一…...

嵌入式面试常问问题

以下内容面向嵌入式/系统方向的初学者与面试备考者,全面梳理了以下几大板块,并在每个板块末尾列出常见的面试问答思路,帮助你既能夯实基础,又能应对面试挑战。 一、TCP/IP 协议 1.1 TCP/IP 五层模型概述 链路层(Link Layer) 包括网卡驱动、以太网、Wi‑Fi、PPP 等。负责…...

作为点的对象CenterNet论文阅读

摘要 检测器将图像中的物体表示为轴对齐的边界框。大多数成功的目标检测方法都会枚举几乎完整的潜在目标位置列表&#xff0c;并对每一个位置进行分类。这种做法既浪费又低效&#xff0c;并且需要额外的后处理。在本文中&#xff0c;我们采取了不同的方法。我们将物体建模为单…...