修改了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. 划分二分类 可以根据不同的类别进行多个划分,以…...
逻辑回归:给不确定性划界的分类大师
想象你是一名医生。面对患者的检查报告(肿瘤大小、血液指标),你需要做出一个**决定性判断**:恶性还是良性?这种“非黑即白”的抉择,正是**逻辑回归(Logistic Regression)** 的战场&a…...
3.3.1_1 检错编码(奇偶校验码)
从这节课开始,我们会探讨数据链路层的差错控制功能,差错控制功能的主要目标是要发现并且解决一个帧内部的位错误,我们需要使用特殊的编码技术去发现帧内部的位错误,当我们发现位错误之后,通常来说有两种解决方案。第一…...
基于数字孪生的水厂可视化平台建设:架构与实践
分享大纲: 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年,数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段,基于数字孪生的水厂可视化平台的…...
第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明
AI 领域的快速发展正在催生一个新时代,智能代理(agents)不再是孤立的个体,而是能够像一个数字团队一样协作。然而,当前 AI 生态系统的碎片化阻碍了这一愿景的实现,导致了“AI 巴别塔问题”——不同代理之间…...
【论文阅读28】-CNN-BiLSTM-Attention-(2024)
本文把滑坡位移序列拆开、筛优质因子,再用 CNN-BiLSTM-Attention 来动态预测每个子序列,最后重构出总位移,预测效果超越传统模型。 文章目录 1 引言2 方法2.1 位移时间序列加性模型2.2 变分模态分解 (VMD) 具体步骤2.3.1 样本熵(S…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)
上一章用到了V2 的概念,其实 Fiori当中还有 V4,咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务),代理中间件(ui5-middleware-simpleproxy)-CSDN博客…...
iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈
在日常iOS开发过程中,性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期,开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发,但背后往往隐藏着系统资源调度不当…...
【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的“no matching...“系列算法协商失败问题
【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的"no matching..."系列算法协商失败问题 摘要: 近期,在使用较新版本的OpenSSH客户端连接老旧SSH服务器时,会遇到 "no matching key exchange method found", "n…...
为什么要创建 Vue 实例
核心原因:Vue 需要一个「控制中心」来驱动整个应用 你可以把 Vue 实例想象成你应用的**「大脑」或「引擎」。它负责协调模板、数据、逻辑和行为,将它们变成一个活的、可交互的应用**。没有这个实例,你的代码只是一堆静态的 HTML、JavaScript 变量和函数,无法「活」起来。 …...
