修改了mybatis的xml中的sql不重启服务器如何动态加载更新
目录
一、背景
二、注意
三、代码
四、使用示例
五、其他参考博客
一、背景
开发一个报表功能,好几百行sql,每次修改完想自测下都要重启服务器,启动一次服务器就要3分钟,重启10次就要半小时,耗不起时间呀。于是在网上找半天,没发现能直接用的, 最后还是乖乖用了自己的业余时间,参考了网上内容写了个合适自己的类。
二、注意
1.本类在mybatis-plus-boot-starter 3.4.0, mybatis3.5.5下有效,其他版本没试过
2.部分idea版本修改了xml中的sql后,并不会直接写入到硬盘中,而是保留在内存中,需要手动ctrl+s或者切换到其他窗口才能触发写入新内容到硬盘,所以使用本类时要确认你修改的sql确实已经保存进硬盘里了
3.xml所在文件夹的绝对位置,需要你修改下,再使用本类
三、代码
用一个类就能实现开发阶段sql热更新
这个类可以配置启动一个线程,每10秒重新加载最近有修改过的sql
也可以调用一下接口重新修改过的sql
代码如下:
package com.gree;import com.baomidou.mybatisplus.core.MybatisMapperRegistry;
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.builder.xml.XMLMapperEntityResolver;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.executor.keygen.SelectKeyGenerator;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.parsing.XNode;
import org.apache.ibatis.parsing.XPathParser;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.*;/*** 本类用于热部署mybatis xml中修改的sql* 注意:* 1.本类在mybatis-plus-boot-starter 3.4.0 和 mybatis3.5.5 下有效,其他版本没试过* 2.部分idea版本修改了xml中的sql后,并不会直接写入到硬盘中,而是保留在内存中,需要手动ctrl+s或者切换到其他窗口才能触发写入新内容到硬盘,所以使用本类时要确认你修改的sql确实已经保存进硬盘里了* 3.xml所在文件夹的绝对位置,需要你修改下,再使用本类*/
@RestController
public class MybatisMapperRefresh {//xml所在文件夹的绝对位置(这里改成你的位置)private String mapperPath = "D:\\wjh\\Mome\\openGitCode\\mybatisRefreshDemo\\src\\main\\resources\\mapper";//是否需要启动一个线程,每隔一段时间就刷新下private boolean needStartThread = false;//刷新间隔时间(秒)private int sleepSeconds = 10;//项目启动时间(或加载本class的时间)private long startTime = new Date().getTime();//上次执行更新xml的时间private long lasteUpdateTime = 0;private static final Log logger = LogFactory.getLog(MybatisMapperRefresh.class);private SqlSessionFactory sqlSessionFactory;private Configuration configuration;/*** 构造函数,由spring调用生成bean* @param sqlSessionFactory*/public MybatisMapperRefresh(SqlSessionFactory sqlSessionFactory) {this.sqlSessionFactory = sqlSessionFactory;this.configuration = sqlSessionFactory.getConfiguration();if (needStartThread) {this.startThread();}}/*** 调用这个接口刷新你的sql,接口会返回刷新了哪些xml* @return*/@RequestMapping("/sql/refresh")public List<String> refreshMapper() {List<String> refreshedList = new ArrayList<>();try {refreshedList = refreshDir();} catch (Exception e) {e.printStackTrace();}return refreshedList;}/*** 启动一个线程,每间隔一段时间就更新下xml中的sql*/public void startThread() {new Thread(new Runnable() {@Overridepublic void run() {while (true) {logger.warn("线程循环中!");try {refreshDir();} catch (Exception e) {e.printStackTrace();}try {Thread.sleep(sleepSeconds * 1000);} catch (Exception e) {e.printStackTrace();}}}}, "mybatis-plus MapperRefresh").start();}/*** 刷新指定目录下所有xml文件** @throws Exception*/private List<String> refreshDir() throws Exception {List<String> refreshedList = new ArrayList<>();try {//获取指定目录下,修改时间大于上次刷新时间,并且修改时间大于项目启动时间的xmlList<File> fileList = FileUtil.getAllFiles(mapperPath);ArrayList<File> needUpdateFiles = new ArrayList<>();for (File file : fileList) {long lastModified = file.lastModified();if (file.isFile() && startTime <= lastModified && lasteUpdateTime <= lastModified) {needUpdateFiles.add(file);continue;}}//逐个xml刷新if (needUpdateFiles.size() != 0) {lasteUpdateTime = new Date().getTime();}for (File file : needUpdateFiles) {Resource refresh = refresh(new FileSystemResource(file));if(refresh != null){refreshedList.add(refresh.getFile().getAbsolutePath());}}} catch (Exception e) {e.printStackTrace();}//返回已刷新的文件return refreshedList;}/*** 刷新mapper*/private Resource refresh(Resource resource) throws Exception {//打印一下流,看看有没有获取到更新的内容/*InputStream inputStream = resource.getInputStream();InputStreamReader inputStreamReader = new InputStreamReader(inputStream);BufferedReader bufferedReader = new BufferedReader(inputStreamReader);String line;while ((line = bufferedReader.readLine()) != null) {System.out.println(line);}bufferedReader.close();*/boolean isSupper = configuration.getClass().getSuperclass() == Configuration.class;try {//清理loadedResources//loadedResources:用于注册所有 Mapper XML 配置文件路径Field loadedResourcesField = isSupper? configuration.getClass().getSuperclass().getDeclaredField("loadedResources"): configuration.getClass().getDeclaredField("loadedResources");loadedResourcesField.setAccessible(true);Set<String> loadedResourcesSet = ((Set<String>) loadedResourcesField.get(configuration));loadedResourcesSet.remove(resource.toString());//分析需要刷新的xml文件XPathParser xPathParser = new XPathParser(resource.getInputStream(), true, configuration.getVariables(),new XMLMapperEntityResolver());//得到xml中的mapper节点XNode xNode = xPathParser.evalNode("/mapper");String xNodeNamespace = xNode.getStringAttribute("namespace");//清理mapperRegistry中的knownMappers//mapperRegistry:用于注册 Mapper 接口信息,建立 Mapper 接口的 Class 对象和 MapperProxyFactory 对象之间的关系,其中 MapperProxyFactory 对象用于创建 Mapper 动态代理对象Field knownMappersField = MybatisMapperRegistry.class.getDeclaredField("knownMappers");knownMappersField.setAccessible(true);Map knownMappers = (Map) knownMappersField.get(configuration.getMapperRegistry());knownMappers.remove(Resources.classForName(xNodeNamespace));//清理caches//caches:用于注册 Mapper 中配置的所有缓存信息,其中 Key 为 Cache 的 id,也就是 Mapper 的命名空间,Value 为 Cache 对象configuration.getCacheNames().remove(xNodeNamespace);//其他清理操作cleanParameterMap(xNode.evalNodes("/mapper/parameterMap"), xNodeNamespace);cleanResultMap(xNode.evalNodes("/mapper/resultMap"), xNodeNamespace);cleanKeyGenerators(xNode.evalNodes("insert|update|select|delete"), xNodeNamespace);cleanMappedStatements(xNode.evalNodes("insert|update|select|delete"), xNodeNamespace);cleanSqlElement(xNode.evalNodes("/mapper/sql"), xNodeNamespace);//重新加载xml文件XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(resource.getInputStream(),configuration, resource.toString(),configuration.getSqlFragments());xmlMapperBuilder.parse();logger.warn("重新加载成功: " + resource );return resource;} catch (IOException e) {logger.error("重新加载失败 :" ,e);} finally {ErrorContext.instance().reset();}return null;}/*** 清理parameterMap* parameterMap用于注册 Mapper 中通过 标签注册的参数映射信息。Key 为 ParameterMap 的 id,由 Mapper 命名空间和 标签的 id 属性构成,Value 为解析 标签后得到的 ParameterMap 对象** @param list* @param namespace*/private void cleanParameterMap(List<XNode> list, String namespace) {for (XNode parameterMapNode : list) {String id = parameterMapNode.getStringAttribute("id");configuration.getParameterMaps().remove(namespace + "." + id);}}/*** 清理resultMap* resultMap用于注册 Mapper 配置文件中通过 标签配置的 ResultMap 信息,ResultMap 用于建立 Java 实体属性与数据库字段之间的映射关系,其中 Key 为 ResultMap 的 id,该 id 是由 Mapper 命名空间和 标签的 id 属性组成的,Value 为解析 标签后得到的 ResultMap 对象** @param list* @param namespace*/private void cleanResultMap(List<XNode> list, String namespace) {for (XNode resultMapNode : list) {String id = resultMapNode.getStringAttribute("id", resultMapNode.getValueBasedIdentifier());configuration.getResultMapNames().remove(id);configuration.getResultMapNames().remove(namespace + "." + id);clearResultMap(resultMapNode, namespace);}}/*** 清理ResultMap* ResultMap用于注册 Mapper 配置文件中通过 标签配置的 ResultMap 信息,ResultMap 用于建立 Java 实体属性与数据库字段之间的映射关系,其中 Key 为 ResultMap 的 id,该 id 是由 Mapper 命名空间和 标签的 id 属性组成的,Value 为解析 标签后得到的 ResultMap 对象*/private void clearResultMap(XNode xNode, String namespace) {for (XNode resultChild : xNode.getChildren()) {if ("association".equals(resultChild.getName()) || "collection".equals(resultChild.getName())|| "case".equals(resultChild.getName())) {if (resultChild.getStringAttribute("select") == null) {configuration.getResultMapNames().remove(resultChild.getStringAttribute("id", resultChild.getValueBasedIdentifier()));configuration.getResultMapNames().remove(namespace + "."+ resultChild.getStringAttribute("id", resultChild.getValueBasedIdentifier()));if (resultChild.getChildren() != null && !resultChild.getChildren().isEmpty()) {clearResultMap(resultChild, namespace);}}}}}/*** 清理keyGenerators* keyGenerators:用于注册 KeyGenerator,KeyGenerator 是 MyBatis 的主键生成器,MyBatis 提供了三种KeyGenerator,即 Jdbc3KeyGenerator(数据库自增主键)、NoKeyGenerator(无自增主键)、SelectKeyGenerator(通过 select 语句查询自增主键,例如 oracle 的 sequence)** @param list* @param namespace*/private void cleanKeyGenerators(List<XNode> list, String namespace) {for (XNode xNode : list) {String id = xNode.getStringAttribute("id");configuration.getKeyGeneratorNames().remove(id + SelectKeyGenerator.SELECT_KEY_SUFFIX);configuration.getKeyGeneratorNames().remove(namespace + "." + id + SelectKeyGenerator.SELECT_KEY_SUFFIX);}}/*** 清理MappedStatements* MappedStatement 对象描述 <insert|selectlupdateldelete> 等标签或者通过 @Select|@Delete|@Update|@Insert 等注解配置的 SQL 信息。MyBatis 将所有的 MappedStatement 对象注册到该属性中,其中 Key 为 Mapper 的 Id, Value 为 MappedStatement 对象** @param list* @param namespace*/private void cleanMappedStatements(List<XNode> list, String namespace) {Collection<MappedStatement> mappedStatements = configuration.getMappedStatements();List<MappedStatement> objects = new ArrayList<>();for (XNode xNode : list) {String id = xNode.getStringAttribute("id");Iterator<MappedStatement> it = mappedStatements.iterator();while (it.hasNext()) {Object object = it.next();if (object instanceof org.apache.ibatis.mapping.MappedStatement) {MappedStatement mappedStatement = (MappedStatement) object;if (mappedStatement.getId().equals(namespace + "." + id)) {objects.add(mappedStatement);}}}}mappedStatements.removeAll(objects);}/*** 清理sql节点缓存* 用于注册 Mapper 中通过 标签配置的 SQL 片段,Key 为 SQL 片段的 id,Value 为 MyBatis 封装的表示 XML 节点的 XNode 对象** @param list* @param namespace*/private void cleanSqlElement(List<XNode> list, String namespace) {for (XNode context : list) {String id = context.getStringAttribute("id");configuration.getSqlFragments().remove(id);configuration.getSqlFragments().remove(namespace + "." + id);}}public static class FileUtil {/*** 列出执行文件夹下的所有文件,包含子目录文件*/public static List<File> getAllFiles(String folderPath) {List<File> fileList = new ArrayList<>();File folder = new File(folderPath);if (!folder.exists() || !folder.isDirectory()) {return fileList;}File[] files = folder.listFiles();for (File file : files) {if (file.isFile()) {fileList.add(file);} else if (file.isDirectory()) {fileList.addAll(getAllFiles(file.getAbsolutePath()));}}return fileList;}}
}
pom.xml文件也贴出来给大家参考
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.greetree</groupId><artifactId>mybatisRefreshDemo</artifactId><version>1.0-SNAPSHOT</version><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.4.0</version><relativePath/> <!-- lookup parent from repository --></parent><properties><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.0</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.25</version></dependency><dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper-spring-boot-starter</artifactId><version>1.3.0</version></dependency></dependencies></project>
四、使用示例
1. 将类MybatisMapperRefresh粘贴到可以被spring扫描到的任意目录
2. 修改类中的mapperPath,改成你的xml文件所在目录
3. 启动服务器
4. 修改你的xml文件里的sql
5.ctrl+s保存文件,确保idea将修改内容写入到硬盘,而不是在内存中
6.调用接口http://localhost:{你项目端口号}/sql/refresh 更新sql,接口会返回刷新了哪些xml
7.验证你的sql是否热更新了
五、其他参考博客
IDEA的热部署【MyBatis XML热部署 】_怎么配热部署实现更新ibatis的xml文件-CSDN博客
相关文章:
修改了mybatis的xml中的sql不重启服务器如何动态加载更新
目录 一、背景 二、注意 三、代码 四、使用示例 五、其他参考博客 一、背景 开发一个报表功能,好几百行sql,每次修改完想自测下都要重启服务器,启动一次服务器就要3分钟,重启10次就要半小时,耗不起时间呀。于是在…...
Intel和AMD用户再等等!微软确认Win11 24H2年底前登陆
微软近日确认,Windows 11 24H2版本将于2024年底前正式登陆使用英特尔和AMD处理器的PC。 根据微软介绍,Windows 11 24H2将作为传统功能更新,将在今年晚些时候提供给所有设备。 此前,微软已向搭载骁龙X Plus和X Elite系列处理器的Co…...
Web开发:图片九宫格与非九宫格动态切换效果(HTML、CSS、JavaScript)
目录 一、业务需求 二、实现思路 三、实现过程 1、基础页面 2、图片大小调整 3、图片位置调整 4、鼠标控制切换 5、添加过渡 四、完整代码 一、业务需求 默认显示基础图片; 当鼠标移入,使用九宫格效果展示图片; 当鼠标离开&#…...
价格较低,功能最强?OpenAI 推出 GPT-4o mini,一个更小、更便宜的人工智能模型
OpenAI美东时间周四推出“GPT-4o mini”,入局“小而精”AI模型竞争,称这款新模型是“功能最强、成本偏低的模型”,计划今后整合图像、视频、音频到这个模型中。 OpenAI表示,GPT-4o mini 相较于 OpenAI 目前最先进的 AI 模型更加便…...
【学习笔记】无人机系统(UAS)的连接、识别和跟踪(八)-无人机探测与避让(DAA)机制
目录 引言 5.6 探测与避让(DAA)机制 5.6.1 基于PC5的探测与避让(DAA)机制 引言 3GPP TS 23.256 技术规范,主要定义了3GPP系统对无人机(UAV)的连接性、身份识别、跟踪及A2X(Airc…...
网络结构-组件-AI(九)
深度学习网络组件 RNN公式讲解计算示意图讲解 CNN计算示意 Normalization(归一化层)Normalization常见两种方式 Dropout层 RNN 循环神经网络(recurrent neural network) 主要思想: 即将整个序列划分成多个时间步,将每一个时间步的…...
弹性网络回归(Elastic Net Regression)
弹性网络回归(Elastic Net Regression)的详细理论知识推导 理论背景 弹性网络回归结合了岭回归(Ridge Regression)和Lasso回归(Lasso Regression)的优点,通过引入两个正则化参数来实现特征选择…...
【深度学习】FaceChain-SuDe,免训练,AI换脸
https://arxiv.org/abs/2403.06775 FaceChain-SuDe: Building Derived Class to Inherit Category Attributes for One-shot Subject-Driven Generation 摘要 最近,基于主体驱动的生成技术由于其个性化文本到图像生成的能力,受到了广泛关注。典型的研…...
Uniapp鸿蒙项目实战
Uniapp鸿蒙项目实战 24.7.6 Dcloud发布了uniapp兼容鸿蒙的文档:Uniapp开发鸿蒙应用 在实际使用中发现一些问题,开贴记录一下 设备准备 windows电脑准备(家庭版不行,教育版、企业版、专业版也可以,不像uniapp说的只有…...
计算机三级嵌入式笔记(一)—— 嵌入式系统概论
目录 考点1 嵌入式系统 考点2 嵌入式系统的组成与分类 考点3 嵌入式系统的分类与发展 考点4 SOC芯片 考点5 数字(电子)文本 考点6 数字图像 考点7 数字音频与数字视频 考点8 数字通信 考点9 计算机网络 考点10 互联网 考纲(2023&am…...
react Jsx基础概念和本质
什么是jsx jsx是JavaScript和XML(HTML)的缩写,表示在js代码中编写HTML模板结构,它是react中编写UI模板的方式 const message this is message function App(){return (<div><h1>this is title</h1>{message}</div>) } jsx优…...
【深大计算机系统(2)】实验一 实验环境配置与使用 附常用指令
目录 一、 实验目标: 二、实验环境与工件: 三、实验内容与步骤 1. 学习并熟悉Linux基本操作,按照要求创建用户。(30分) 2.新建用户主目录下创建子目录:gdbdebug,并进入gdbdebug子目录。将过程和…...
目标检测经典模型之YOLOV5-detect.py源码解析(持续更新)
detect文件框架 一、导入模块包二、定义run函数1. 归一化操作代码解析uint8精度转换归一化 2. 扩展维度为什么扩展维度?代码解释 3. 对检测结果类别计数检查是否有检测结果统计每个类别的出现次数构建描述性字符串 三、定义命令行参数四、主函数 本帖是YOLOV5推理部…...
PF4J+SpringBoot
plugin-common pom.xml相关配置 <groupId>pub.qingyun</groupId> <artifactId>plugin-common</artifactId> <version>0.0.1-SNAPSHOT</version> <description>插件配置类</description><dependency><groupId>or…...
设计模式11-原型模式
设计模式11-原型模式 写在前面对象创建模式典型模式原型模式动机结构代码推导应用特点要点总结 原型模式与工厂方法模式对比工厂方法模式原型模式什么时候用什么模式 写在前面 对象创建模式 通过对象创建模式绕开动态内存分配来避免创建过程中所导致的耦合过紧的问题。从而支…...
Tomcat长连接源码解析
长连接: 客户端发送Http请求至服务端,请求发送完之后socket连接不断开,可以继续接收下一个Http请求并且解析返回。接手并解析这些Http请求的时候socket连接不断开,这种过程被称为长连接。 需要注意的点就在于,在满足什么条件的情况…...
C++编程:实现一个跨平台安全的定时器Timer模块
文章目录 0. 概要1. 设计目标2. SafeTimer 类的实现2.1 头文件 safe_timer.h源文件 safe_timer.cpp 3. 工作流程图4. 单元测试 0. 概要 对于C应用编程,定时器模块是一个至关重要的组件。为了确保系统的可靠性和功能安全,我们需要设计一个高效、稳定的定…...
PyTorch的自动微分模块【含梯度基本数学原理详解】
文章目录 1、简介1.1、基本概念1.2、基本原理1.2.1、自动微分1.2.2、梯度1.2.3、梯度求导1.2.4、梯度下降法1.2.5、张量梯度举例 1.3、Autograd的高级功能 2、梯度基本计算2.1、单标量梯度2.2、单向量梯度的计算2.3、多标量梯度计算2.4、多向量梯度计算 3、控制梯度计算4、累计…...
AI 绘画|Midjourney设计Logo提示词
你是否已经看过许多别人分享的 MJ 咒语,却仍无法按照自己的想法画图?通过学习 MJ 的提示词逻辑后,你将能够更好地理解并创作自己的“咒语”。本文将详细拆解使用 MJ 设计 Logo 的逻辑,让你在阅读后即可轻松上手,制作出…...
LeNet实验 四分类 与 四分类变为多个二分类
目录 1. 划分二分类 2. 训练独立的二分类模型 3. 二分类预测结果代码 4. 二分类预测结果 5 改进训练模型 6 优化后 预测结果代码 7 优化后预测结果 8 训练四分类模型 9 预测结果代码 10 四分类结果识别 1. 划分二分类 可以根据不同的类别进行多个划分,以…...
【BUG】已解决:java.lang.reflect.InvocationTargetException
已解决:java.lang.reflect.InvocationTargetException 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页,我是博主英杰,211科班出身,就职于医疗科技公司,热衷分享知识,武汉城市开发…...
配置kali 的apt命令在线安装包的源为国内源
目录 一、安装VMware Tools 二、配置apt国内源 一、安装VMware Tools 点击安装 VMware Tools 后,会加载一个虚拟光驱,里面包含 VMware Tools 的安装包 鼠标右键单击 VMware Tools 的安装包,点击复制到 点击 主目录,再点击选择…...
JAVA 异步编程(线程安全)二
1、线程安全 线程安全是指你的代码所在的进程中有多个线程同时运行,而这些线程可能会同时运行这段代码,如果每次运行的代码结果和单线程运行的结果是一样的,且其他变量的值和预期的也是一样的,那么就是线程安全的。 一个类或者程序…...
Golang | Leetcode Golang题解之第260题只出现一次的数字III
题目: 题解: func singleNumber(nums []int) []int {xorSum : 0for _, num : range nums {xorSum ^ num}lsb : xorSum & -xorSumtype1, type2 : 0, 0for _, num : range nums {if num&lsb > 0 {type1 ^ num} else {type2 ^ num}}return []in…...
IDEA自带的Maven 3.9.x无法刷新http nexus私服
问题: 自建的私服,配置了域名,使用http协议,在IDEA中或本地Maven 3.9.x会出现报错,提示http被blocked,原因是Maven 3.8.1开始,Maven默认禁止使用HTTP仓库地址,只允许使用HTTPS仓库地…...
56、本地数据库迁移到阿里云
现有需求,本地数据库迁移到阿里云上。 库名xy102表 test01test02test01 test023条数据。1、登录阿里云界面创建免费试用ECS实列。 阿里云登录页 (aliyun.com)](https://account.aliyun.com/login/login.htm?oauth_callbackhttps%3A%2F%2Fusercenter2.aliyun.com%…...
新时代多目标优化【数学建模】领域的极致探索——数学规划模型
目录 例1 1.问题重述 2.基本模型 变量定义: 目标函数: 约束条件: 3.模型分析与假设 4.模型求解 5.LINGO代码实现 6.结果解释 编辑 7.敏感性分析 8.结果解释 例2 奶制品的销售计划 1.问题重述 编辑 2.基本模型 3.模…...
单例模式详解
文章目录 一、概述1.单例模式2.单例模式的特点3.单例模式的实现方法 二、单例模式的实现1. 饿汉式2. 懒汉式3. 双重校验锁4. 静态内部类5. 枚举 三、总结 一、概述 1.单例模式 单例模式(Singleton Pattern)是一种创建型设计模式,确保一个类…...
WebGIS主流的客户端框架比较|OpenLayers|Leaflet|Cesium
实现 WebGIS 应用的主流前端框架主要包括 OpenLayers、Leaflet、Mapbox GL JS 和 Cesium 等。每个框架都有其独特的功能和优势,适合不同的应用场景。 WebGIS主流前端框架的优缺点 前 端 框架优点缺点OpenLayers较重量级的开源库,二维GIS功能最丰富全面…...
【LabVIEW作业篇 - 2】:分数判断、按钮控制while循环暂停、单击按钮获取book文本
文章目录 分数判断按钮控制while循环暂停按钮控制单个while循环暂停 按钮控制多个while循环暂停单击按钮获取book文本 分数判断 限定整型数值输入控件值得输入范围,范围在0-100之间,判断整型数值输入控件的输入值。 输入范围在0-59之间,显示…...
互联网软件外包平台/湖南专业seo优化
redis的底层数据结构1. redis底层用了什么数据结构1.1 示例一:String类型1.2 示例二: List类型2. 五种数据类型底层用了什么数据结构2.1 String(SDS)2.2 List(linkedlist、ziplist)2.3 hash(hashtable、ziplist &#…...
客户说做网站没效果/网络营销薪酬公司
作者:生信宝典 | 生信宝典公众号原文:https://mp.weixin.qq.com/s/OkOgN4j44MHNt1_noPVpzAConda 为什么越来越慢?Conda 中包含的软件越来越多,而且软件的不同版本都保留了下来,软件的索引文件越来越大,安装…...
海口网站建设网站制作/网络推广和网站推广平台
Sentinel限流熔断—解决限流/XXXX/{id}查询接口不准确的问题 问题分析 Sentinel中HTTP服务的限流默认由Sentinel-Web-Servlet包中的CommonFilter来实现,从代码中可以看到,这个Filter会把每个不同的URL都作为不同的资源来处理。 在下面这段代码中&#…...
谷歌做英文网站/女教师遭网课入侵直播录屏曝光i
课程内容是完整的,只是目录收集和编号的有点乱,大家下载后的都是正常的,不用担心(28)\\02-跟着江哥狂虐H5跨平台开发系列-认识HTML-第一次更新\\视频;目录中文件数:9个├─(28) 07-HTML作用(掌握)-李南江.mp4├─(29) 08-HTML发展…...
室内设计师资格证书/seo技术教程
1.在Java中,如果父类中的某些方法不包含任何逻辑,并且需要有子类重写,应该使用(c)关键字来申明父类的这些方法。 a. Finalc b. Static c. Abstract d. Void 2.给定两个java程序,如下: public…...
深圳品牌模板网站建设/哈尔滨新闻头条今日新闻
一、什么是跨域问题 当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域 如图:我们的vue前端项目的端口号为8081,ssm后端项目的端口号为8080,恭喜你出现跨域问题了! 这时,我们可能会想到改…...