网站建设分为哪几个阶段/深圳搜索竞价账户托管
文章目录
- Pre
- 工程结构
- 概述
- 1. 插件接口与实现分析
- 2. 插件工厂初始化分析
- 3. 插件项包装类解析
- 4. 插件工厂方法解析
- 5. 插件加载与资源释放机制
- 6. 实现类
- 小结
- 附PluginFactory

Pre
插件 - 通过SPI方式实现插件管理
插件 - 一份配置,离插件机制只有一步之遥
插件 - 插件机制触手可及
Plugin - 插件开发01_SPI的基本使用
Plugin - 插件开发02_使用反射机制和自定义配置实现插件化开发
Plugin - 插件开发03_Spring Boot动态插件化与热加载
Plugin - 插件开发04_Spring Boot中的SPI机制与Spring Factories实现
Plugin - 插件开发05_Solon中的插件实现机制
工程结构
概述
接下来我们主要对IPlugin接口及其实现,以及插件的加载和管理机制进行分析,分为如下几个点
- 插件接口与实现分析
- 插件工厂初始化分析
- 插件项包装类解析
- 插件工厂方法解析
- 插件加载与资源释放机制
1. 插件接口与实现分析
插件机制的核心接口是IPlugin
, IPlugin
继承了AutoCloseable
接口,确保插件可以被正确关闭。
此外,还有一个IDefaultPlugin
接口,继承了IPlugin,用于定义默认插件。
接口的主要代码如下:
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ClassUtil;
import com.alibaba.fastjson.JSONObject;import java.util.HashMap;
import java.util.Map;/*** 插件模块接口** @author bwcx_jzy* @since 2021/12/22*/
public interface IPlugin extends AutoCloseable {/*** 执行插件方法** @param main 拦截到到对象* @param parameter 执行方法传人的参数* @return 返回值* @throws Exception 异常*/Object execute(Object main, Map<String, Object> parameter) throws Exception;/*** 执行插件方法** @param main 主参数* @param parameters 其他参数* @return 结果* @throws Exception 异常*/default Object execute(Object main, Object... parameters) throws Exception {// 处理参数int length = parameters.length;Map<String, Object> map = new HashMap<>(length / 2);for (int i = 0; i < length; i += 2) {map.put(parameters[i].toString(), parameters[i + 1]);}return this.execute(main, map);}/*** 执行插件方法** @param main 拦截到到对象* @param parameters 其他参数* @param <T> 泛型* @param cls 返回值类型* @return 返回值* @throws Exception 异常*/default <T> T execute(Object main, Class<T> cls, Object... parameters) throws Exception {Object execute = this.execute(main, parameters);return this.convertResult(execute, cls);}/*** 执行插件方法** @param main 拦截到到对象* @param parameter 执行方法传人的参数* @param <T> 泛型* @param cls 返回值类型* @return 返回值* @throws Exception 异常*/default <T> T execute(Object main, Map<String, Object> parameter, Class<T> cls) throws Exception {Object execute = this.execute(main, parameter);return this.convertResult(execute, cls);}/*** 转换结果** @param execute 结果* @param cls 返回值类型* @param <T> 泛型* @return 返回值类型*/@SuppressWarnings("unchecked")default <T> T convertResult(Object execute, Class<T> cls) {if (execute == null) {return null;}Class<?> aClass = execute.getClass();if (ClassUtil.isSimpleValueType(aClass)) {return (T) Convert.convert(aClass, execute);}// json 数据Object o = JSONObject.toJSON(execute);if (o instanceof JSONObject) {JSONObject jsonObject = (JSONObject) o;return jsonObject.toJavaObject(cls);}return (T) execute;}/*** 系统关闭,插件资源释放** @throws Exception 异常*/@Overridedefault void close() throws Exception {}
}
主要方法解析
-
execute 方法:
execute(Object main, Class<T> cls, Object... parameters)
:此方法接收一个对象、一个返回值类型和一组参数,执行插件方法后返回指定类型的结果。execute(Object main, Map<String, Object> parameter, Class<T> cls)
:此方法接收一个对象、一个参数Map和一个返回值类型,执行插件方法后返回指定类型的结果。
-
convertResult 方法:
- 该方法用于将执行结果转换为指定类型。它首先判断结果是否为简单类型,如果是则直接转换;否则将结果转换为JSON对象,再将JSON对象转换为指定类型。
-
close 方法:
- 实现了
AutoCloseable
接口,用于在系统关闭时释放插件资源。默认实现为空方法。
- 实现了
2. 插件工厂初始化分析
插件工厂类PluginFactory
负责插件的初始化和管理 ,实现了ApplicationContextInitializer
和ApplicationListener
接口,用于在Spring上下文初始化和关闭时进行插件的加载和资源释放。
初始化核心方法的主要代码如下:
@Overridepublic void initialize(ConfigurableApplicationContext applicationContext) {init();// 扫描插件 实现Set<Class<?>> classes = ClassUtil.scanPackage("io.jpom", IPlugin.class::isAssignableFrom);List<PluginItemWrap> pluginItemWraps = classes.stream().filter(aClass -> ClassUtil.isNormalClass(aClass) && aClass.isAnnotationPresent(PluginConfig.class)).map(aClass -> new PluginItemWrap((Class<? extends IPlugin>) aClass)).filter(pluginItemWrap -> {if (StrUtil.isEmpty(pluginItemWrap.getName())) {DefaultSystemLog.getLog().warn("plugin config name error:{}", pluginItemWrap.getClassName());return false;}return true;}).collect(Collectors.toList());//Map<String, List<PluginItemWrap>> pluginMap = CollStreamUtil.groupByKey(pluginItemWraps, PluginItemWrap::getName);pluginMap.forEach((key, value) -> {// 排序value.sort((o1, o2) -> Comparator.comparingInt((ToIntFunction<PluginItemWrap>) value1 -> {Order order = value1.getClassName().getAnnotation(Order.class);if (order == null) {return 0;}return order.value();}).compare(o1, o2));PLUGIN_MAP.put(key, value);});log.debug("load plugin count:{}", pluginMap.keySet().size());}
3. 插件项包装类解析
PluginItemWrap
类用于封装插件的相关信息,如插件配置、插件名、插件类名和插件对象,实现了对插件的包装和插件实例的创建。
import cn.hutool.core.util.ReflectUtil;
import cn.jiangzeyin.common.spring.SpringUtil;
import lombok.Getter;/*** 插件端对象** @author bwcx_jzy* @since 2021/12/24*/
@Getter
public class PluginItemWrap {/*** 配置相关*/private final PluginConfig pluginConfig;/*** 插件名*/private final String name;/*** 插件类名*/private final Class<? extends IPlugin> className;/*** 插件对象*/private volatile IPlugin plugin;public PluginItemWrap(Class<? extends IPlugin> className) {this.className = className;this.pluginConfig = className.getAnnotation(PluginConfig.class);this.name = this.pluginConfig.name();}public IPlugin getPlugin() {if (plugin == null) {synchronized (className) {if (plugin == null) {//boolean nativeObject = this.pluginConfig.nativeObject();if (nativeObject) {plugin = ReflectUtil.newInstance(className);} else {plugin = SpringUtil.getBean(className);}}}}return plugin;}
}
4. 插件工厂方法解析
插件工厂提供了若干静态方法供外部使用,如获取插件对象、判断是否包含某个插件以及获取插件数量等。
public static IPlugin getPlugin(String name) {List<PluginItemWrap> pluginItemWraps = PLUGIN_MAP.get(name);PluginItemWrap first = CollUtil.getFirst(pluginItemWraps);Assert.notNull(first, "对应找到对应到插件:" + name);return first.getPlugin();
}public static boolean contains(String name) {return PLUGIN_MAP.containsKey(name);
}public static int size() {return PLUGIN_MAP.size();
}
5. 插件加载与资源释放机制
在PluginFactory
文件中,实现了插件的加载和资源释放机制。在init
方法中,扫描指定包下的插件并加载。在onApplicationEvent
方法中,监听Spring上下文关闭事件并释放插件资源。
这两个方法的主要代码如下:
private static void init() {File runPath = JpomManifest.getRunPath().getParentFile();File plugin = FileUtil.file(runPath, "plugin");if (!plugin.exists() || plugin.isFile()) {return;}// 加载二级插件包File[] dirFiles = plugin.listFiles(File::isDirectory);if (dirFiles != null) {for (File file : dirFiles) {File lib = FileUtil.file(file, "lib");if (!lib.exists() || lib.isFile()) {continue;}File[] listFiles = lib.listFiles((dir, name) -> StrUtil.endWith(name, FileUtil.JAR_FILE_EXT, true));if (listFiles == null || listFiles.length <= 0) {continue;}for (File listFile : listFiles) {addPlugin(file.getName(), listFile);}}}// 加载一级独立插件端包File[] files = plugin.listFiles(pathname -> FileUtil.isFile(pathname) && FileUtil.JAR_FILE_EXT.equalsIgnoreCase(FileUtil.extName(pathname)));if (files != null) {for (File file : files) {addPlugin(file.getName(), file);}}
}@Override
public void onApplicationEvent(ContextClosedEvent event) {Collection<List<PluginItemWrap>> values = PLUGIN_MAP.values();for (List<PluginItemWrap> value : values) {for (PluginItemWrap pluginItemWrap : value) {IPlugin plugin = pluginItemWrap.getPlugin();IoUtil.close(plugin);}}
}
6. 实现类
我们以DefaultDbH2PluginImpl
插件实现分析插件的视线 ,它实现了IPlugin
接口中定义的方法。
@PluginConfig(name = "db-h2")
public class DefaultDbH2PluginImpl implements IDefaultPlugin {@Overridepublic Object execute(Object main, Map<String, Object> parameter) throws Exception {String method = StrUtil.toString(main);if (StrUtil.equals("backupSql", method)) {String url = (String) parameter.get("url");String user = (String) parameter.get("user");String password = (String) parameter.get("pass");String backupSqlPath = (String) parameter.get("backupSqlPath");List<String> tableNameList = (List<String>) parameter.get("tableNameList");this.backupSql(url, user, password, backupSqlPath, tableNameList);} else if (StrUtil.equals("restoreBackupSql", method)) {String backupSqlPath = (String) parameter.get("backupSqlPath");DataSource dataSource = (DataSource) parameter.get("dataSource");if (dataSource == null) {// 加载数据源dataSource = DSFactory.get();}this.restoreBackupSql(backupSqlPath, dataSource);} else if (StrUtil.equals("recoverToSql", method)) {File dbPath = (File) parameter.get("dbPath");String dbName = (String) parameter.get("dbName");File recoverBackup = (File) parameter.get("recoverBackup");return this.recover(dbPath, dbName, recoverBackup);} else {throw new IllegalArgumentException("不支持的类型");}return "done";}/*** 恢复** @param dbPath 数据库路径* @param dbName 数据库名* @param recoverBackup 恢复到哪个路径* @return 返回恢复到 sql 文件* @throws SQLException sql*/private File recover(File dbPath, String dbName, File recoverBackup) throws SQLException {String dbLocalPath = FileUtil.getAbsolutePath(dbPath);ArrayList<String> list = FileLister.getDatabaseFiles(dbLocalPath, dbName, true);if (CollUtil.isEmpty(list)) {return null;}FileUtil.mkdir(recoverBackup);// 备份数据for (String s : list) {FileUtil.move(FileUtil.file(s), recoverBackup, true);}String absolutePath = FileUtil.getAbsolutePath(recoverBackup);Console.log("h2 db recover backup path,{}", absolutePath);// 恢复数据Recover recover = new Recover();recover.runTool("-dir", absolutePath, "-db", dbName);return FileUtil.file(recoverBackup, dbName + ".h2.sql");}/*** 备份 SQL** @param url jdbc url* @param user user* @param password password* @param backupSqlPath backup SQL file path, absolute path* @param tableNameList backup table name list, if need backup all table, use null*/private void backupSql(String url, String user, String password,String backupSqlPath, List<String> tableNameList) throws SQLException {// 备份 SQLString sql = StrUtil.format("SCRIPT DROP to '{}'", backupSqlPath);// 判断是否部分部分表if (!CollectionUtils.isEmpty(tableNameList)) {String tableNames = StrUtil.join(StrUtil.COMMA, tableNameList.toArray());sql = StrUtil.format("{} TABLE {}", sql, tableNames);}DefaultSystemLog.getLog().debug("backup SQL is: {}", sql);// 执行 SQL 备份脚本Shell shell = new Shell();/*url 表示 h2 数据库的 jdbc url* user 表示登录的用户名* password 表示登录密码* driver 是 jdbc 驱动* sql 是备份的 sql 语句* - 案例:script drop to ${fileName1} table ${tableName1},${tableName2}...* - script drop to 表示备份数据库,drop 表示建表之前会先删除表* - ${fileName1} 表示备份之后的文件名* - table 表示需要备份的表名称,后面跟多个表名,用英文逗号分割*/String[] params = new String[]{"-url", url,"-user", user,"-password", password,"-driver", "org.h2.Driver","-sql", sql};try (FastByteArrayOutputStream arrayOutputStream = new FastByteArrayOutputStream()) {try (PrintStream printStream = new PrintStream(arrayOutputStream)) {shell.setOut(printStream);shell.runTool(params);}}}/*** 还原备份 SQL** @param backupSqlPath backup SQL file path, absolute path* @throws SQLException SQLException* @throws IOException FileNotFoundException*/private void restoreBackupSql(String backupSqlPath, DataSource dataSource) throws SQLException, IOException {Assert.notNull(dataSource, "Restore Backup sql error...H2 DataSource not null");try (Connection connection = dataSource.getConnection()) {// 读取数据库备份文件,执行还原File backupSqlFile = FileUtil.file(backupSqlPath);try (FileReader fileReader = new FileReader(backupSqlFile)) {RunScript.execute(connection, fileReader);}}}
}
小结
在Jpom项目中,IPlugin
接口是插件系统的核心接口,它定义了插件需要实现的基本方法。 在插件工厂类PluginFactory
中,通过扫描类路径加载插件并进行初始化:
IPlugin
接口及其实现为Jpom的插件系统提供了灵活的扩展能力。通过定义通用的执行方法和结果转换方法,IPlugin
接口简化了插件的开发和集成过程。而插件工厂类PluginFactory
则通过扫描和加载机制,实现了插件的自动发现和初始化,使得插件系统具有高度的可扩展性和灵活性。
通过对插件接口IPlugin、插件工厂PluginFactory、插件项包装类PluginItemWrap以及插件的加载和资源释放机制的分析,可以清晰地了解整个工程的插件机制。这为后续的插件开发和管理提供了坚实的基础。
附PluginFactory
@Slf4j
public class PluginFactory implements ApplicationContextInitializer<ConfigurableApplicationContext>, ApplicationListener<ContextClosedEvent> {// private static final List<FeatureCallback> FEATURE_CALLBACKS = new ArrayList<>();private static final Map<String, List<PluginItemWrap>> PLUGIN_MAP = new ConcurrentHashMap<>();// /**
// * 添加回调事件
// *
// * @param featureCallback 回调
// */
// public static void addFeatureCallback(FeatureCallback featureCallback) {
// FEATURE_CALLBACKS.add(featureCallback);
// }
//
// public static List<FeatureCallback> getFeatureCallbacks() {
// return FEATURE_CALLBACKS;
// }/*** 获取插件端** @param name 插件名* @return 插件对象*/public static IPlugin getPlugin(String name) {List<PluginItemWrap> pluginItemWraps = PLUGIN_MAP.get(name);PluginItemWrap first = CollUtil.getFirst(pluginItemWraps);Assert.notNull(first, "对应找到对应到插件:" + name);return first.getPlugin();}/*** 判断是否包含某个插件** @param name 插件名* @return true 包含*/public static boolean contains(String name) {return PLUGIN_MAP.containsKey(name);}/*** 插件数量** @return 当前加载的插件数量*/public static int size() {return PLUGIN_MAP.size();}/*** 正式环境添加依赖*/private static void init() {File runPath = JpomManifest.getRunPath().getParentFile();File plugin = FileUtil.file(runPath, "plugin");if (!plugin.exists() || plugin.isFile()) {return;}// 加载二级插件包File[] dirFiles = plugin.listFiles(File::isDirectory);if (dirFiles != null) {for (File file : dirFiles) {File lib = FileUtil.file(file, "lib");if (!lib.exists() || lib.isFile()) {continue;}File[] listFiles = lib.listFiles((dir, name) -> StrUtil.endWith(name, FileUtil.JAR_FILE_EXT, true));if (listFiles == null || listFiles.length <= 0) {continue;}addPlugin(file.getName(), lib);}}// 加载一级独立插件端包File[] files = plugin.listFiles(pathname -> FileUtil.isFile(pathname) && FileUtil.JAR_FILE_EXT.equalsIgnoreCase(FileUtil.extName(pathname)));if (files != null) {for (File file : files) {addPlugin(file.getName(), file);}}}private static void addPlugin(String pluginName, File file) {DefaultSystemLog.getLog().info("加载:{} 插件", pluginName);ClassLoader contextClassLoader = ClassLoaderUtil.getClassLoader();JarClassLoader.loadJar((URLClassLoader) contextClassLoader, file);}@Overridepublic void initialize(ConfigurableApplicationContext applicationContext) {init();// 扫描插件 实现Set<Class<?>> classes = ClassUtil.scanPackage("io.jpom", IPlugin.class::isAssignableFrom);List<PluginItemWrap> pluginItemWraps = classes.stream().filter(aClass -> ClassUtil.isNormalClass(aClass) && aClass.isAnnotationPresent(PluginConfig.class)).map(aClass -> new PluginItemWrap((Class<? extends IPlugin>) aClass)).filter(pluginItemWrap -> {if (StrUtil.isEmpty(pluginItemWrap.getName())) {DefaultSystemLog.getLog().warn("plugin config name error:{}", pluginItemWrap.getClassName());return false;}return true;}).collect(Collectors.toList());//Map<String, List<PluginItemWrap>> pluginMap = CollStreamUtil.groupByKey(pluginItemWraps, PluginItemWrap::getName);pluginMap.forEach((key, value) -> {// 排序value.sort((o1, o2) -> Comparator.comparingInt((ToIntFunction<PluginItemWrap>) value1 -> {Order order = value1.getClassName().getAnnotation(Order.class);if (order == null) {return 0;}return order.value();}).compare(o1, o2));PLUGIN_MAP.put(key, value);});log.debug("load plugin count:{}", pluginMap.keySet().size());}@Overridepublic void onApplicationEvent(ContextClosedEvent event) {Collection<List<PluginItemWrap>> values = PLUGIN_MAP.values();for (List<PluginItemWrap> value : values) {for (PluginItemWrap pluginItemWrap : value) {IPlugin plugin = pluginItemWrap.getPlugin();IoUtil.close(plugin);}}}
}
相关文章:

Plugin - 插件开发06_开源项目JPom中的插件实现机制
文章目录 Pre工程结构概述1. 插件接口与实现分析2. 插件工厂初始化分析3. 插件项包装类解析4. 插件工厂方法解析5. 插件加载与资源释放机制6. 实现类小结附PluginFactory Pre 插件 - 通过SPI方式实现插件管理 插件 - 一份配置,离插件机制只有一步之遥 插件 - 插件…...

关于成功插入 SQLite 但没有数据的问题
背景 技术栈:SpringBoot Mybatis-flex SQLite 项目中集成了SQLite,配置如下: spring:datasource:url: jdbc:sqlite::resource:db/project.dbdriver-class-name: org.sqlite.JDBC在进行测试时,使用Mybatis-flex往表中插入数据&…...

单片机+Qt上位机
目录 一、引言 通信方式 优势 案例 常见问题及解决方法 二、单片机与 Qt 上位机的通信方式 (一)使用 QT 上位机和 STC 单片机实现串口通信 三、单片机 Qt 上位机的优势 (一)高效便捷的 USB 通信上位机解决方案 …...

C++ 类和对象(中)
1.类的六个默认成员函数 如果一个类中什么成员都没有,简称为空类。 空类中真的什么都没有吗?其实并不是,任何类在什么都不写时,编译器会自动生成以下六个默认成员函数。 默认成员函数:用户没有显式实现,编…...

在做题中学习(79):最小K个数
解法:快速选择算法 说明:堆排序也是经典解决问题的算法,但时间复杂度为:O(NlogK),K为k个元素 而将要介绍的快速选择算法的时间复杂度为: O(N) 先看我的前两篇文章,分别学习:数组分三块&#…...

spark3 sql优化:同一个表关联多次,优化方案
目录 1.合并查询2.使用 JOIN 条件的过滤优化3.使用 Map-side Join 或 Broadcast Join4.使用 Partitioning 和 Bucketing5.利用 DataFrame API 进行优化假设 A 和 B 已经加载为 DataFramePerform left joins with specific conditions6.使用缓存或持久化7.避免笛卡尔积总结 1.合…...

JavaWeb学习(4)(四大域、HttpSession原理(面试)、SessionAPI、Session实现验证码功能)
目录 一、web四大域。 (1)基本介绍。 (2)RequestScope。(请求域) (3)SessionScope。(会话域) (4)ApplicationScope。(应用域) (5)PageScope。(页面域) 二、Ht…...

Ubuntu22.04系统源码编译OpenCV 4.10.0(包含opencv_contrib)
因项目需要使用不同版本的OpenCV,而本地的Ubuntu22.04系统装了ROS2自带OpenCV 4.5.4的版本,于是编译一个OpenCV 4.10.0(带opencv_contrib)版本,给特定的项目使用,这就不用换个设备后重新安装OpenCV 了&…...

【Unity高级】在编辑器中如何让物体围绕一个点旋转固定角度
本文介绍如何在编辑器里让物体围绕一个点旋转固定角度,比如上图里的Cube是围绕白色圆盘的中心旋转45度的。 目标: 创建一个在 Unity 编辑器中使用的旋转工具,使开发者能够在编辑模式下快速旋转一个物体。 实现思路: 编辑模式下…...

2024.11.29——[HCTF 2018]WarmUp 1
拿到题,发现是一张图,查看源代码发现了被注释掉的提示 <!-- source.php--> step 1 在url传参看看这个文件,发现了这道题的源码 step 2 开始审计代码,分析关键函数 //mb_strpos($haystack,$needle,$offset,$encoding):int|…...

AGameModeBase和游戏模式方法
AGameModeBase和游戏模式方法有着密切的关系: AGameModeBase是游戏模式的基础类: 它提供了控制游戏规则的基本框架包含了一系列管理游戏流程的核心方法是所有自定义游戏模式类的父类 主要的游戏模式方法包括: // 游戏初始化时调用 virtua…...

Swift 扩展
Swift 扩展 Swift 是一种强大的编程语言,由苹果公司开发,用于iOS、macOS、watchOS和tvOS应用程序的开发。自2014年发布以来,Swift因其易于阅读和编写的语法、现代化的设计以及出色的性能而广受欢迎。本文将探讨Swift的一些关键特性ÿ…...

【NebulaGraph】官方查询语言nGQL教程1 (四)
【NebulaGraph】官方查询语言nGQL教程1 1. 课程信息2. 查找路径FIND PATH2.1 补充说明FIND PATH2.2 例子 1. 课程信息 课程地址: https://www.bilibili.com/video/BV1PT411P7w8/?spm_id_from333.337.search-card.all.click&vd_source240d9002f7c7e3da63cd9a975639409a …...

阿里云负载均衡SLB实践
基于上篇文章继续,如果你使用的是阿里云等云平台,通过配置nginxkeepAlived行不通,因为阿里云服务器不支持你虚拟出ip提供给外部访问,需要使用阿里云的负载均衡产品 对应的产品有三个系列 1、应用场景 ALB: 主要是对应应用层的7层…...

鸿蒙技术分享:❓❓[鸿蒙应用开发]怎么更好的管理模块生命周期?
鸿蒙HarmonyOS NEXT应用开发架构设计-模块生命周期管理 模块化开发 模块化开发已经是应用开发中的一个共识,一般对于公司级的应用开发,都会考虑是否可以进行模块化开发。 HarmonyOS NEXT系统应用开发目前使用的Stage模型其实就有涉及模块化开发的部分…...

深度解析 Ansible:核心组件、配置、Playbook 全流程与 YAML 奥秘(上)
文章目录 一、ansible的主要组成部分二、安装三、相关文件四、ansible配置文件五、ansible 系列 一、ansible的主要组成部分 ansible playbook:任务剧本(任务集),编排定义ansible任务集的配置文件,由ansible顺序依次执…...

LabVIEW气缸摩擦力测试系统
基于LabVIEW的气缸摩擦力测试系统实现了气缸在不同工作状态下摩擦力的快速、准确测试。系统由硬件平台和软件两大部分组成,具有高自动化、精确测量和用户友好等特点,可广泛应用于精密机械和自动化领域。 项目背景: 气缸作为舵机关键部件…...

Leetcode. 688骑士在棋盘上的概率
题目描述 原题链接:Leetcode. 688骑士在棋盘上的概率 解题思路 多元dp 将dp[step][i][j])定义为从(i, j)出发,走step步之后骑士还在棋盘上的概率。 如果 ( i , j ) (i,j) (i,j)不在棋盘上,即非 0 < i < n 0<i<n 0<i<…...

TCP/IP 协议栈高效可靠的数据传输机制——以 Linux 4.19 内核为例
TCP/IP 协议栈是一种非常成熟且广泛使用的网络通信框架,它将复杂的网络通信任务分成多个层次,从而简化设计,使每一层的功能更加清晰和独立。在经典的 TCP/IP 协议栈中,常见的分层为链路层、网络层、传输层和应用层。本文将对每一层的基本功能进行描述,并列出对应于 Linux …...

Ubuntu22.04搭建LAMP环境(linux服务器学习笔记)
目录 引言: 一、系统更新 二、安装搭建Apache2 1.你可以通过以下命令安装它: 2.查看Apache2版本 3.查看Apache2运行状态 4.浏览器访问 三、安装搭建MySQL 1.安装MySQL 2.查看MySQL 版本 3.安全配置MySQL 3.1是否设置密码?(按y|Y表…...

鸿蒙面试---1208
HarmonyOS 三大技术理念 分布式架构:HarmonyOS 的分布式架构使得设备之间能够无缝协同工作。例如,它允许用户在不同的智能设备(如手机、平板、智能手表等)之间共享数据和功能。比如,用户可以在手机上开始编辑文档&…...

java基础教程第16篇( 正则表达式)
Java 正则表达式 正则表达式定义了字符串的模式。 正则表达式可以用来搜索、编辑或处理文本。 正则表达式并不仅限于某一种语言,但是在每种语言中有细微的差别。 Java 提供了 java.util.regex 包,它包含了 Pattern 和 Matcher 类,用于处理正…...

Docker部署的gitlab升级的详细步骤(升级到17.6.1版本)
文章目录 一、Gitlab提示升级信息二、老版本的docker运行gitlab命令三、备份老版本Gitlab数据四、确定升级路线五、升级(共分3个版本升级)5.1 升级第一步(17.1.2 > 17.3.7)5.2 升级第二步(17.3.7 > 17.5.3)5.3 升级第三步(17.5.3 > 17.6.1) 六、web端访问gitlab服务 一…...

【如何制定虚拟货币的补仓策略并计算回本和盈利】
在虚拟货币市场中,价格波动性极大,如何在波动中生存并获得盈利是每个投资者都在思考的问题。作为一种投资策略,补仓(又称“摊低成本”)常常被用来降低持仓成本,并在市场回升时获得更大的盈利。但如何科学地设定补仓计划,确定回本点和盈利目标呢? 本文将以 Dogecoin 为…...

给图像去除水印攻
去除水印的过程与添加水印相反,它涉及到图像修复、颜色匹配和区域填充等技术。OpenCV-Python 提供了多种方法来处理不同类型的水印,包括但不限于纯色水印、半透明水印以及复杂背景上的水印。下面将详细介绍几种常见的去水印策略,并给出具体的…...

Linux之封装线程库和线程的互斥
Linux之封装线程库和线程的互斥与同步 一.封装线程库二.线程的互斥2.1互斥量的概念2.2初始化和销毁互斥量2.3加锁和解锁2.4互斥量的原理2.5可重入和线程安全2.6死锁 一.封装线程库 其实在我们C内部也有一个线程库而C中的线程库也是封装的原生线程库的函数,所以我们…...

PH热榜 | 2024-12-08
1. Reforged Labs 标语:轻松为手游工作室制作AI广告。 介绍:Reforged Labs 推出了一款前所未有的AI视频制作服务。我们自动化了以往昂贵且耗时的创意流程,取而代之的是能快速、低成本地为各个工作室量身定制视频广告。 产品网站࿱…...

LeetCode刷题day20——贪心
LeetCode刷题day20——贪心 435. 无重叠区间763. 划分字母区间分析: 56. 合并区间分析: 435. 无重叠区间 给定一个区间的集合 intervals ,其中 intervals[i] [starti, endi] 。返回 需要移除区间的最小数量,使剩余区间互不重叠 …...

CCF编程能力等级认证GESP—C++3级—20241207
CCF编程能力等级认证GESP—C3级—20241207 单选题(每题 2 分,共 30 分)判断题(每题 2 分,共 20 分)编程题 (每题 25 分,共 50 分)数字替换打印数字 单选题(每题 2 分,共 …...

Microi 吾码:大数据浪潮中的智能领航者
目录 一、大数据时代的挑战与机遇 二、Microi 吾码在大数据存储方面的应用 与分布式文件系统的集成 数据库存储优化 三、Microi 吾码在大数据处理与分析中的应用 数据清洗与转换 数据分析与挖掘 四、Microi 吾码在大数据可视化中的应用 五、Microi 吾码在大数据流式处…...