(二)MyBatis源码阅读:SqlSession分析
一、核心流程
以下代码便是MyBatis的核心流程,我们从该代码出发分析MyBatis的源码。
@Testpublic void test2() throws Exception{// 1.获取配置文件InputStream in = Resources.getResourceAsStream("mybatis-config.xml");// 2.加载解析配置文件并获取SqlSessionFactory对象SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);// 3.根据SqlSessionFactory对象获取SqlSession对象SqlSession sqlSession = factory.openSession();// 4.通过SqlSession中提供的 API方法来操作数据库UserMapper mapper = sqlSession.getMapper(UserMapper.class);List<User> list = mapper.selectUserList();for (User user : list) {System.out.println(user);}// 5.关闭会话sqlSession.close();}
二、SqlSessionFactory创建流程
(一)SqlSessionFactoryBuilder
SqlSessionFactoryBuilder调用了XMLConfigBuilder 的parse方法进行了解析。我们接下来去看XMLConfigBuilder类。
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {try {// 用于解析 mybatis-config.xml,同时创建了 Configuration 对象 >>XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);// 解析XML,最终返回一个 DefaultSqlSessionFactory >>return build(parser.parse());} catch (Exception e) {throw ExceptionFactory.wrapException("Error building SqlSession.", e);} finally {ErrorContext.instance().reset();try {inputStream.close();} catch (IOException e) {// Intentionally ignore. Prefer previous error.}}}
(二)XMLConfigBuilder
构造方法:
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {super(new Configuration()); // 完成了Configuration的初始化ErrorContext.instance().resource("SQL Mapper Configuration");this.configuration.setVariables(props); // 设置对应的Properties属性this.parsed = false; // 设置 是否解析的标志为 falsethis.environment = environment; // 初始化environmentthis.parser = parser; // 初始化 解析器}
我们去看看configuration里面干了什么,里面主要为各个类取了别名。
parseConfiguration方法:
parseConfiguration方法就是对各个具体的方法进行了封装,我们任意找几个方法分析即可。
private void parseConfiguration(XNode root) {try {//issue #117 read properties first// 对于全局配置文件各种标签的解析propertiesElement(root.evalNode("properties"));// 解析 settings 标签Properties settings = settingsAsProperties(root.evalNode("settings"));// 读取文件loadCustomVfs(settings);// 日志设置loadCustomLogImpl(settings);// 类型别名typeAliasesElement(root.evalNode("typeAliases"));// 插件pluginElement(root.evalNode("plugins"));// 用于创建对象objectFactoryElement(root.evalNode("objectFactory"));// 用于对对象进行加工objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));// 反射工具箱reflectorFactoryElement(root.evalNode("reflectorFactory"));// settings 子标签赋值,默认值就是在这里提供的 >>settingsElement(settings);// read it after objectFactory and objectWrapperFactory issue #631// 创建了数据源 >>environmentsElement(root.evalNode("environments"));databaseIdProviderElement(root.evalNode("databaseIdProvider"));typeHandlerElement(root.evalNode("typeHandlers"));// 解析引用的Mapper映射器mapperElement(root.evalNode("mappers"));} catch (Exception e) {throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);}}
propertiesElement方法:
private void propertiesElement(XNode context) throws Exception {if (context != null) {// 创建了一个 Properties 对象,后面可以用到Properties defaults = context.getChildrenAsProperties();String resource = context.getStringAttribute("resource");String url = context.getStringAttribute("url");if (resource != null && url != null) {// url 和 resource 不能同时存在throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other.");}// 加载resource或者url属性中指定的 properties 文件if (resource != null) {defaults.putAll(Resources.getResourceAsProperties(resource));} else if (url != null) {defaults.putAll(Resources.getUrlAsProperties(url));}Properties vars = configuration.getVariables();if (vars != null) {// 和 Configuration中的 variables 属性合并defaults.putAll(vars);}// 更新对应的属性信息parser.setVariables(defaults);configuration.setVariables(defaults);}}
settingsAsProperties方法:
private Properties settingsAsProperties(XNode context) {if (context == null) {return new Properties();}// 获取settings节点下的所有的子节点Properties props = context.getChildrenAsProperties();// Check that all settings are known to the configuration classMetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory);for (Object key : props.keySet()) {//if (!metaConfig.hasSetter(String.valueOf(key))) {throw new BuilderException("The setting " + key + " is not known. Make sure you spelled it correctly (case sensitive).");}}return props;}
mapperElement方法:
调用 Configuration的addMapper方法,将接口放入MapperRegistry中。MapperRegistry是接口与接口工程的map键值对,便于之后创建对应的接口。
MapperRegistry就是记录接口与工厂的关系。
addMapper方法调用了解析器的parse方法。
(三)XMLMapperBuilder
parse方法
public void parse() {// 总体上做了两件事情,对于语句的注册和接口的注册if (!configuration.isResourceLoaded(resource)) {// 1、具体增删改查标签的解析。// 一个标签一个MappedStatement。 >>configurationElement(parser.evalNode("/mapper"));configuration.addLoadedResource(resource);// 2、把namespace(接口类型)和工厂类绑定起来,放到一个map。// 一个namespace 一个 MapperProxyFactory >>bindMapperForNamespace();}parsePendingResultMaps();parsePendingCacheRefs();parsePendingStatements();}
(四)创建流程图
三、SqlSession 创建流程
通过DefaultSqlSessionFactory的openSession方法来创建对应的SqlSession。在这个DefaultSqlSession对象中包括了Configuration和Executor对象
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {Transaction tx = null;try {final Environment environment = configuration.getEnvironment();// 获取事务工厂final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);// 创建事务tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);// 根据事务工厂和默认的执行器类型,创建执行器 >>final Executor executor = configuration.newExecutor(tx, execType);return new DefaultSqlSession(configuration, executor, autoCommit);} catch (Exception e) {closeTransaction(tx); // may have fetched a connection so lets call close()throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);} finally {ErrorContext.instance().reset();}}
执行的具体流程如下:
四、获取代理对象流程
DefaultSqlSession调用 MapperRegistry的getMapper方法,该方法拿到对应的代理工厂,创建对应的代理对象。
进入newInstance方法,最终我们在代码中发现代理对象是通过JDK动态代理创建,返回的代理对象。而且里面也传递了一个实现了InvocationHandler接口的触发管理类。
执行的具体流程如下:
五、SQL语句执行流程
由于所有的Mapper都是JDK动态代理对象,所以任意的方法都是执行触发管理类MapperProxy的invoke()方法。
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {// toString hashCode equals getClass等方法,无需走到执行SQL的流程if (Object.class.equals(method.getDeclaringClass())) {return method.invoke(this, args);} else {// 提升获取 mapperMethod 的效率,到 MapperMethodInvoker(内部接口) 的 invoke// 普通方法会走到 PlainMethodInvoker(内部类) 的 invokereturn cachedInvoker(method).invoke(proxy, method, args, sqlSession);}} catch (Throwable t) {throw ExceptionUtil.unwrapThrowable(t);}
}
然后进入到PlainMethodInvoker的invoke方法
@Overridepublic Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {// SQL执行的真正起点return mapperMethod.execute(sqlSession, args);}
进入mapperMethod.execute()方法。在这一步,根据不同的type(INSERT、UPDATE、DELETE、SELECT)和返回类型:(1)调用convertArgsToSqlCommandParam()将方法参数转换为SQL的参数。(2)调用sqlSession的insert()、update()、delete()、selectOne ()方法。我们以查询为例,会走到selectOne()方法。
public Object execute(SqlSession sqlSession, Object[] args) {Object result;switch (command.getType()) { // 根据SQL语句的类型调用SqlSession对应的方法case INSERT: {// 通过 ParamNameResolver 处理args[] 数组 将用户传入的实参和指定参数名称关联起来Object param = method.convertArgsToSqlCommandParam(args);// sqlSession.insert(command.getName(), param) 调用SqlSession的insert方法// rowCountResult 方法会根据 method 字段中记录的方法的返回值类型对结果进行转换result = rowCountResult(sqlSession.insert(command.getName(), param));break;}case UPDATE: {Object param = method.convertArgsToSqlCommandParam(args);result = rowCountResult(sqlSession.update(command.getName(), param));break;}case DELETE: {Object param = method.convertArgsToSqlCommandParam(args);result = rowCountResult(sqlSession.delete(command.getName(), param));break;}case SELECT:if (method.returnsVoid() && method.hasResultHandler()) {// 返回值为空 且 ResultSet通过 ResultHandler处理的方法executeWithResultHandler(sqlSession, args);result = null;} else if (method.returnsMany()) {result = executeForMany(sqlSession, args);} else if (method.returnsMap()) {result = executeForMap(sqlSession, args);} else if (method.returnsCursor()) {result = executeForCursor(sqlSession, args);} else {// 返回值为 单一对象的方法Object param = method.convertArgsToSqlCommandParam(args);// 普通 select 语句的执行入口 >>result = sqlSession.selectOne(command.getName(), param);if (method.returnsOptional()&& (result == null || !method.getReturnType().equals(result.getClass()))) {result = Optional.ofNullable(result);}}break;case FLUSH:result = sqlSession.flushStatements();break;default:throw new BindingException("Unknown execution method for: " + command.getName());}if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {throw new BindingException("Mapper method '" + command.getName()+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");}return result;}
这里来到了对外的接口的默认实现类DefaultSqlSession。selectOne()最终也是调用了selectList()。
@Overridepublic <T> T selectOne(String statement, Object parameter) {// 来到了 DefaultSqlSession// Popular vote was to return null on 0 results and throw exception on too many.List<T> list = this.selectList(statement, parameter);if (list.size() == 1) {return list.get(0);} else if (list.size() > 1) {throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());} else {return null;}}
在SelectList()中,我们先根据command name(Statement ID)从Configuration中拿到MappedStatement。ms里面有xml中增删改查标签配置的所有属性,包括id、statementType、sqlSource、useCache、入参、出参等等。然后执行了Executor的query()方法。Executor是第二步openSession的时候创建的,创建了执行器基本类型之后,依次执行了二级缓存装饰,和插件包装。
@Overridepublic <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {try {MappedStatement ms = configuration.getMappedStatement(statement);// 如果 cacheEnabled = true(默认),Executor会被 CachingExecutor装饰return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);} catch (Exception e) {throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);} finally {ErrorContext.instance().reset();}}
BaseExecutor.query方法
@Overridepublic <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)throws SQLException {Cache cache = ms.getCache();// cache 对象是在哪里创建的? XMLMapperBuilder类 xmlconfigurationElement()// 由 <cache> 标签决定if (cache != null) {// flushCache="true" 清空一级二级缓存 >>flushCacheIfRequired(ms);if (ms.isUseCache() && resultHandler == null) {ensureNoOutParams(ms, boundSql);// 获取二级缓存// 缓存通过 TransactionalCacheManager、TransactionalCache 管理@SuppressWarnings("unchecked")List<E> list = (List<E>) tcm.getObject(cache, key);if (list == null) {list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);// 写入二级缓存tcm.putObject(cache, key, list); // issue #578 and #116}return list;}}// 走到 SimpleExecutor | ReuseExecutor | BatchExecutorreturn delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);}public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {// 异常体系之 ErrorContextErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());if (closed) {throw new ExecutorException("Executor was closed.");}if (queryStack == 0 && ms.isFlushCacheRequired()) {// flushCache="true"时,即使是查询,也清空一级缓存clearLocalCache();}List<E> list;try {// 防止递归查询重复处理缓存queryStack++;// 查询一级缓存// ResultHandler 和 ResultSetHandler的区别list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;if (list != null) {handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);} else {// 真正的查询流程list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);}} finally {queryStack--;}if (queryStack == 0) {for (DeferredLoad deferredLoad : deferredLoads) {deferredLoad.load();}// issue #601deferredLoads.clear();if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {// issue #482clearLocalCache();}}return list;}
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {List<E> list;// 先占位localCache.putObject(key, EXECUTION_PLACEHOLDER);try {// 三种 Executor 的区别,看doUpdate// 默认Simplelist = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);} finally {// 移除占位符localCache.removeObject(key);}// 写入一级缓存localCache.putObject(key, list);if (ms.getStatementType() == StatementType.CALLABLE) {localOutputParameterCache.putObject(key, parameter);}return list;}
我们到具体的实现类SimpleExecutor中查看doQuery方法
@Overridepublic <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {Statement stmt = null;try {Configuration configuration = ms.getConfiguration();// 注意,已经来到SQL处理的关键对象 StatementHandler >>StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);// 获取一个 Statement对象stmt = prepareStatement(handler, ms.getStatementLog());// 执行查询return handler.query(stmt, resultHandler);} finally {// 用完就关闭closeStatement(stmt);}}
在configuration.newStatementHandler()中,new一个StatementHandler,先得到RoutingStatementHandler。RoutingStatementHandler里面没有任何的实现,是用来创建基本的StatementHandler的。这里会根据MappedStatement里面的statementType决定StatementHandler的类型。默认是PREPARED(STATEMENT、PREPARED、CALLABLE)。
StatementHandler里面包含了处理参数的ParameterHandler和处理结果集的ResultSetHandler。
这两个对象都是在上面new的时候创建的。
进入到PreparedStatementHandler中处理
@Overridepublic <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {PreparedStatement ps = (PreparedStatement) statement;// 到了JDBC的流程ps.execute();// 处理结果集return resultSetHandler.handleResultSets(ps);}
执行的具体流程如下:
相关文章:

(二)MyBatis源码阅读:SqlSession分析
一、核心流程 以下代码便是MyBatis的核心流程,我们从该代码出发分析MyBatis的源码。 Testpublic void test2() throws Exception{// 1.获取配置文件InputStream in Resources.getResourceAsStream("mybatis-config.xml");// 2.加载解析配置文件并获取Sq…...

小白学Pytorch系列-- torch.autograd API
小白学Pytorch系列-- torch.autograd API torch.Autograd提供了实现任意标量值函数的自动微分的类和函数。它只需要对现有代码进行最小的更改-你只需要声明张量s,它的梯度应该用requires gradTrue关键字计算。到目前为止,我们只支持浮点张量类型(half, f…...

【大数据基础】基于零售交易数据的Spark数据处理与分析
环境搭建 sudo apt-get install python3-pip pip3 install bottle数据预处理 首先,将数据集E_Commerce_Data.csv上传至hdfs上,命令如下: ./bin/hdfs dfs -put /home/hadoop/E_Commerce_Data.csv /user/hadoop接着,使用如下命令…...

【机器学习】P14 Tensorflow 使用指南 Dense Sequential Tensorflow 实现
Tensorflow 第一节:使用指南Tensorflow 安装神经网络一些基本概念隐藏层和输出层:神经元的输出公式Tensorflow 全连接层 Dense 与 顺序模型 SequentialDense LayerSequential Model代码实现一个神经网络实现方式一:手写神经网络* 实现方式二&…...

ubuntu18.04安装nvidia驱动,3种方式图文详解+卸载教程
教程目录一、关闭secure boot二、禁用nouveau驱动2.1 创建配置文件2.2 添加内容2.3 重启电脑2.4 输入命令验证三、安装显卡驱动3.1 软件和更新(失败)3.2 PPA源安装3.3 官网安装包安装四、卸载显卡驱动笔记本类型Ubuntu系统显卡版本联想拯救者Y7000win10U…...

多线程进阶学习11------CountDownLatch、CyclicBarrier、Semaphore详解
CountDownLatch ①. CountDownLatch主要有两个方法,当一个或多个线程调用await方法时,这些线程会阻塞 ②. 其它线程调用countDown方法会将计数器减1(调用countDown方法的线程不会阻塞) ③. 计数器的值变为0时,因await方法阻塞的线程会被唤醒,继续执行 public static void m…...

华为OD机试用java实现 -【RSA 加密算法】
最近更新的博客 华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为od机试,独家整理 已参加机试人员的实战技巧本篇题解:RSA 加密算法 题目 RSA 加密…...
技术宅小伙:大龄程序员就业,未来我们将何去何从?
程序员是一个高薪高压的职业,同时也是一个需要不断学习的职业。随着技术的不断更新换代,程序员需要不断地学习新的知识和技能,以适应市场的需求。然而,有些程序员可能会遭遇裁员,失去了稳定的收入来源。有些程序员可能…...

Spring Boot+Vue实现Socket通知推送
目录 Spring Boot端 第一步,引入依赖 第二步,创建WebSocket配置类 第三步,创建WebSocket服务 第四步,创建Controller进行发送测试 Vue端 第一步,创建连接工具类 第二步,建立连接 编辑 第三步&a…...

python---python介绍
python介绍 1.1介绍 1.1.1为什么学习 1.1.2什么是python 优雅简单易学1.1.3在线2进制转换 在线二进制转文本工具 - 转换 1.1.4python的安装和配置 1.需要配置对应的环境变量。可以设置多个。 默认全选 设置安装的路径 最后安装完成即可。 验证:python 如何退出 1.1.…...
第十四届蓝桥杯大赛——真题训练第10天
目录 第一题:扫雷 题目描述 输入描述 输出描述 输入输出样例 运行限制 题目代码 第 2 题:完全平方数 问题描述 输入格式 输出格式 样例输入 1 样例输出 1 样例输入 2 样例输出 2 题目分析 题目代码 第三题:求阶乘 问题描述…...

3.29~3.30学习总结
刷题情况: 蓝桥杯刷题: Java学习情况: 抽象类:如果将子类中抽取的共性行为(方法),子类的执行不一样的时候 (通俗一点来说,就是无法找到一个万能的方法体供子类使用,但这…...

结构体详解 ——— C语言
目录 1.结构体 结构体类型的声明 结构的自引用 结构体变量的定义和初始化 结构体内存对齐 结构体传参 结构体实现位段(位段的填充&可移植性) 位段的内存分配 1.结构体 结构体类型的声明 结构的自引用 结构体变量的定义和初始化 结构体内存对…...
Java SE 基础(4) Java的语言特点
语言特点 Java是一门面向对象编程语言,不仅吸收了C语言的各种优点,还摒弃了C里难以理解的多继承、指针等概念,因此Java语言具有功能强大和简单易用两个特征。Java语言作为静态面向对象编程语言的代表,极好地实现了面向对象理论&a…...
都炸店了,拼多多还在坚持什么
子超这两天听说了拼多多被“炸店”事件,第一反应是震惊:这都什么年代了,还有这种不择手段的暴力行为?所谓的炸店,就是一些人员被煽动和组织起来,有预谋地对店铺发起打砸行动,这和线下去打砸商铺…...

vue尚品汇商城项目-day01【6.Footer组件的显示与隐藏】
文章目录6.Footer组件的显示与隐藏6.1我们可以根据组件身上的$route获取当前路由的信息,通过路由路径判断Footer显示与隐藏6.2配置路由的时候,可以给路由添加元信息[meta],路由需要配置对象,它的key不能乱接、瞎写、胡写ÿ…...
命令行上的数据科学第二版 一、简介
原文:https://datascienceatthecommandline.com/2e/chapter-1-introduction.html 贡献者:Ting-xin 这本书是关于如何利用命令行做数据科学的。我的目标是通过教你使用命令行的力量,让自己成为一名更高效和多产的数据科学家。 在标题中同时使…...
utf-8转换到utf-16的转换过程你懂吗?
人生自是有情痴,此恨不关风与月。——唐代元稹《离思》 从UTF-8编码的文件中读取文本并将其存储到Java的String对象中,涉及到从字节序列到Unicode码点,再到UTF-16编码的转换。以下是详细的步骤: 从文件读取字节序列:首…...
C++编程大师之路:从入门到精通--持续更新中~
文章目录前言主要内容C基础入门初识C第一个C程序注释变量常量关键字标识符命名规则数据类型整型sizeof关键字实型(浮点型)字符型转义字符字符串型布尔类型 bool数据的输入运算符算术运算符赋值运算符比较运算符逻辑运算符程序流程结构选择结构if语句三目…...

面试阿里软件测试岗,收到offer后我却毫不犹豫拒绝了....
我大学学的是计算机专业,毕业的时候,对于找工作比较迷茫,也不知道当时怎么想的,一头就扎进了一家外包公司,一干就是2年。我想说的是,但凡有点机会,千万别去外包! 在深思熟虑过后&am…...

Prompt Tuning、P-Tuning、Prefix Tuning的区别
一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...

简易版抽奖活动的设计技术方案
1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...
day52 ResNet18 CBAM
在深度学习的旅程中,我们不断探索如何提升模型的性能。今天,我将分享我在 ResNet18 模型中插入 CBAM(Convolutional Block Attention Module)模块,并采用分阶段微调策略的实践过程。通过这个过程,我不仅提升…...

华为OD机试-食堂供餐-二分法
import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…...

现代密码学 | 椭圆曲线密码学—附py代码
Elliptic Curve Cryptography 椭圆曲线密码学(ECC)是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础,例如椭圆曲线数字签…...
【决胜公务员考试】求职OMG——见面课测验1
2025最新版!!!6.8截至答题,大家注意呀! 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:( B ) A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...

使用 SymPy 进行向量和矩阵的高级操作
在科学计算和工程领域,向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能,能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作,并通过具体…...
大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计
随着大语言模型(LLM)参数规模的增长,推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长,而KV缓存的内存消耗可能高达数十GB(例如Llama2-7B处理100K token时需50GB内存&a…...
重启Eureka集群中的节点,对已经注册的服务有什么影响
先看答案,如果正确地操作,重启Eureka集群中的节点,对已经注册的服务影响非常小,甚至可以做到无感知。 但如果操作不当,可能会引发短暂的服务发现问题。 下面我们从Eureka的核心工作原理来详细分析这个问题。 Eureka的…...

以光量子为例,详解量子获取方式
光量子技术获取量子比特可在室温下进行。该方式有望通过与名为硅光子学(silicon photonics)的光波导(optical waveguide)芯片制造技术和光纤等光通信技术相结合来实现量子计算机。量子力学中,光既是波又是粒子。光子本…...