从头到尾说一次 Spring 事务管理(器) | 京东云技术团队
事务管理,一个被说烂的也被看烂的话题,还是八股文中的基础股之一。
本文会从设计角度,一步步的剖析 Spring 事务管理的设计思路(都会设计事务管理器了,还能玩不转?)
为什么需要事务管理?
先看看如果没有事务管理器的话,如果想让多个操作(方法/类)处在一个事务里应该怎么做:
// MethodA:
public void methodA(){Connection connection = acquireConnection();try{int updated = connection.prepareStatement().executeUpdate();methodB(connection);connection.commit();}catch (Exception e){rollback(connection);}finally {releaseConnection(connection);}
}// MethodB:
public void methodB(Connection connection){int updated = connection.prepareStatement().executeUpdate();
}
或者用 ThreadLocal 存储 Connection?
static ThreadLocal<Connection> connHolder = new ThreadLocal<>();// MethodA:
public void methodA(){Connection connection = acquireConnection();connHolder.set(connection);try{int updated = connection.prepareStatement().executeUpdate();methodB();connection.commit();}catch (Exception e){rollback(connection);}finally {releaseConnection(connection);connHolder.remove();}
}// MethodB:
public void methodB(){Connection connection = connHolder.get();int updated = connection.prepareStatement().executeUpdate();
}
还是有点恶心,再抽象一下?将绑定 Connection 的操作提取为公共方法:
static ThreadLocal<Connection> connHolder = new ThreadLocal<>();private void bindConnection(){Connection connection = acquireConnection();connHolder.set(connection);
}private void unbindConnection(){releaseConnection(connection);connHolder.remove();
}// MethodA:
public void methodA(){try{bindConnection();int updated = connection.prepareStatement().executeUpdate();methoB();connection.commit();}catch (Exception e){rollback(connection);}finally {unbindConnection();}
}// MethodB:
public void methodB(){Connection connection = connHolder.get();int updated = connection.prepareStatement().executeUpdate();
}
现在看起来好点了,不过我有一个新的需求:想让 methodB 独立一个新事务,单独提交和回滚,不影响 methodA
这……可就有点难搞了,ThreadLocal 中已经绑定了一个 Connection,再新事务的话就不好办了
那如果再复杂点呢,methodB 中需要调用 methodC,methodC 也需要一个独立事务……
而且,每次 bind/unbind 的操作也有点太傻了,万一哪个方法忘了写 unbind ,最后来一个连接泄露那不是完蛋了!
好在 Spring 提供了事务管理器,帮我们解决了这一系列痛点。
Spring 事务管理解决了什么问题?
Spring 提供的事务管理可以帮我们管理事务相关的资源,比如 JDBC 的 Connection、Hibernate 的 Session、Mybatis 的 SqlSession。如说上面的 Connection 绑定到 ThreadLocal 来解决共享一个事务的这种方式,Spring 事务管理就已经帮我们做好了。
还可以帮我们处理复杂场景下的嵌套事务,比如前面说到的 methodB/methodC 独立事务。
什么是嵌套事务?
还是拿上面的例子来说, methodA 中调用了 methodB,两个方法都有对数据库的操作,而且都需要事务:
// MethodA:
public void methodA(){int updated = connection.prepareStatement().executeUpdate();methodB();// ...
}// MethodB:
public void methodB(){// ...
}
这种多个方法调用链中都有事务的场景,就是嵌套事务。不过要注意的是,并不是说多个方法使用一个事务才叫嵌套,哪怕是不同的事务,只要在这个方法的调用链中,都是嵌套事务。
什么是事务传播行为?
那调用链中的子方法,是用一个新事务,还是使用当前事务呢?这个子方法决定使用新事务还是当前事务(或不使用事务)的策略,就叫事务传播。
在 Spring 的事务管理中,这个子方法的事务处理策略叫做事务传播行为(Propogation Behavior)。
有哪些事务传播行为?
Spring 的事务管理支持多种传播行为,这里就不贴了,八股文里啥都有。
但给这些传播行为分类之后,无非是以下三种:
-
优先使用当前事务
-
不使用当前事务,新建事务
-
不使用任何事务
比如上面的例子中,methodB/methodC 独立事务,就属于第 2 种传播行为 - 不使用当前事务,新建事务
看个栗子
以 Spring JDBC + Spring注解版的事务举例。在默认的事务传播行为下,methodA 和 methodB 会使用同一个 Connection,在一个事务中
@Transactional
public void methodA(){jdbcTemplate.batchUpdate(updateSql, params);methodB();
}@Transactional
public void methodB(){jdbcTemplate.batchUpdate(updateSql, params);
}
如果我想让 methodB 不使用 methodA 的事务,自己新建一个连接/事务呢?只需要简单的配置一下 @Transactional 注解:
@Transactional
public void methodA(){jdbcTemplate.batchUpdate(updateSql, params);methodB();
}// 传播行为配置为 - 方式2,不使用当前事务,独立一个新事务
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void methodB(){jdbcTemplate.batchUpdate(updateSql, params);
}
就是这么简单,获取 Connection/多方法共享 Connection/多方法共享+独享 Connection/提交/释放连接之类的操作,完全不需要我们操心,Spring 都替我们做好了。
怎么回滚?
在注解版的事务管理中,默认的的回滚策略是:抛出异常就回滚。这个默认策略挺好,连回滚都帮我们解决了,再也不用手动回滚。
但是如果在嵌套事务中,子方法独立新事务呢?这个时候哪怕抛出异常,也只能回滚子事务,不能直接影响前一个事务
可如果这个抛出的异常不是 sql 导致的,比如校验不通过或者其他的异常,此时应该将当前的事务回滚吗?
这个还真不一定,谁说抛异常就要回滚,异常也不回滚行不行?
当然可以!抛异常和回滚事务本来就是两个问题,可以连在一起,也可以分开处理
// 传播行为配置为 - 方式2,不使用当前事务,独立一个新事务// 指定 Exception 也不会滚
@Transactional(propagation = Propagation.REQUIRES_NEW, noRollbackFor = Exception.class)
public void methodB(){jdbcTemplate.batchUpdate(updateSql, params);
}
每个事务/连接使用不同配置
除了传播和回滚之外,还可以给每个事务/连接使用不同的配置,比如不同的隔离级别:
@Transactional
public void methodA(){jdbcTemplate.batchUpdate(updateSql, params);methodB();
}// 传播行为配置为 - 方式2,不使用当前事务,独立一个新事务
// 这个事务/连接中使用 RC 隔离级别,而不是默认的 RR
@Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_UNCOMMITTED)
public void methodB(){jdbcTemplate.batchUpdate(updateSql, params);
}
除了隔离级别之外,其他的 JDBC Connection 配置当然也是支持的,比如 readOnly。这样一来,虽然我们不用显示的获取 connection/session,但还是可以给嵌套中的每一个事务配置不同的参数,非常灵活。
功能总结
好了,现在已经了解了 Spring 事务管理的所有核心功能,来总结一下这些核心功能点:
-
连接/资源管理 - 无需手动获取资源、共享资源、释放资源
-
嵌套事务的支持 - 支持嵌套事务中使用不同的资源策略、回滚策略
-
每个事务/连接使用不同的配置
事务管理器(TransactionManager)模型
其实仔细想想,事务管理的核心操作只有两个:提交和回滚。前面所谓的传播、嵌套、回滚之类的,都是基于这两个操作。
所以 Spring 将事务管理的核心功能抽象为一个事务管理器(Transaction Manager),基于这个事务管理器核心,可以实现多种事务管理的方式。
这个核心的事务管理器只有三个功能接口:
-
获取事务资源,资源可以是任意的,比如jdbc connection/hibernate mybatis session之类,然后绑定并存储
-
提交事务- 提交指定的事务资源
-
回滚事务- 回滚指定的事务资源
interface PlatformTransactionManager{// 获取事务资源,资源可以是任意的,比如jdbc connection/hibernate mybatis session之类TransactionStatus getTransaction(TransactionDefinition definition)throws TransactionException;// 提交事务void commit(TransactionStatus status) throws TransactionException;// 回滚事务void rollback(TransactionStatus status) throws TransactionException;
}
事务定义 - TransactionDefinition
还记得上面的 @Transactional 注解吗,里面定义了传播行为、隔离级别、回滚策略、只读之类的属性,这个就是一次事务操作的定义。
在获取事务资源时,需要根据这个事务的定义来进行不同的配置:
-
比如配置了使用新事务,那么在获取事务资源时就需要创建一个新的,而不是已有的
-
比如配置了隔离级别,那么在首次创建资源(Connection)时,就需要给 Connection 设置 propagation
-
比如配置了只读属性,那么在首次创建资源(Connection)时,就需要给 Connection 设置 readOnly
为什么要单独用一个 TransactionDefinition 来存储事务定义,直接用注解的属性不行吗?
当然可以,但注解的事务管理只是 Spring 提供的自动挡,还有适合老司机的手动挡事务管理(后面会介绍);手动挡可用不了注解,所以单独建一个事务定义的模型,这样就可以实现通用。
事务状态 - TransactionStatus
那既然嵌套事务下,每个子方法的事务可能不同,所以还得有一个子方法事务的状态 - TransactionStatus,用来存储当前事务的一些数据和状态,比如事务资源(Connection)、回滚状态等。
获取事务资源
事务管理器的第一步,就是根据事务定义来获取/创建资源了,这一步最麻烦的是要区分传播行为,不同传播行为下的逻辑不太一样。
“默认的传播行为下,使用当前事务”,怎么算有当前事务呢?
把事务资源存起来嘛,只要已经存在那就是有当前事务,直接获取已存储的事务资源就行。文中开头的例子也演示了,如果想让多个方法无感的使用同一个事务,可以用 ThreadLocal 存储起来,简单粗暴。
Spring 也是这么做的,不过它实现的更复杂一些,抽象了一层事务资源同步管理器 - TransactionSynchronizationManager(本文后面会简称 TxSyncMgr),在这个同步管理器里使用 ThreadLocal 存储了事务资源(本文为了方便理解,尽可能的不贴非关键源码)。
剩下的就是根据不同传播行为,执行不同的策略了,分类之后只有 3 个条件分支:
-
当前有事务 - 根据不同传播行为处理不同
-
当前没事务,但需要开启新事务
-
彻底不用事务 - 这个很少用
public final TransactionStatus getTransaction(TransactionDefinition definition) {//创建事务资源 - 比如 ConnectionObject transaction = doGetTransaction();if (isExistingTransaction(transaction)) {// 处理当前已有事务的场景return handleExistingTransaction(def, transaction, debugEnabled);}else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED){// 开启新事务return startTransaction(def, transaction, debugEnabled, suspendedResources);}else {// 彻底不用事务}// ...
}
先介绍一下分支 2 - 当前没事务,但需要开启新事务,这个逻辑相对简单一些。只需要新建事务资源,然后绑定到 ThreadLocal 即可:
private TransactionStatus startTransaction(TransactionDefinition definition, Object transaction,boolean debugEnabled, SuspendedResourcesHolder suspendedResources) {// 创建事务DefaultTransactionStatus status = newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);// 开启事务(beginTx或者setAutoCommit之类的操作)// 然后将事务资源绑定到事务资源管理器 TransactionSynchronizationManagerdoBegin(transaction, definition);
现在回到分支1 - 当前有事务 - 根据不同传播行为处理不同,这个就稍微有点麻烦了。因为有子方法独立事务的需求,可是 TransactionSynchronizationManager 却只能存一个事务资源。
挂起(Suspend)和恢复(Resume)
Spring 采用了一种挂起(Suspend) - 恢复(Resume)的设计来解决这个嵌套资源处理的问题。当子方法需要独立事务时,就将当前事务挂起,从 TxSyncMgr 中移除当前事务资源,创建新事务的状态时,将挂起的事务资源保存至新的事务状态 TransactionStatus 中;在子方法结束时,只需要再从子方法的事务状态中,再次拿出挂起的事务资源,重新绑定至 TxSyncMgr 即可完成恢复的操作。
整个挂起 - 恢复的流程,如下图所示:
注意:挂起操作是在获取事务资源这一步做的,而恢复的操作是在子方法结束时(提交或者回滚)中进行的。
这样一来,每个 TransactionStatus 都会保存挂起的前置事务资源,如果方法调用链很长,每次都是新事务的话,那这个 TransactionStatus 看起来就会像一个链表:
提交事务
获取资源、操作完毕后来到了提交事务这一步,这个提交操作比较简单,只有两步:
-
当前是新事务才提交
-
处理挂起资源
怎么知道是新事务?
每经过一次事务嵌套,都会创建一个新的 TransactionStatus,这个事务状态里会记录当前是否是新事务。如果多个子方法都使用一个事务资源,那么除了第一个创建事务资源的 TransactionStatus 之外,其他都不是新事务。
如下图所示,A -> B -> C 时,由于 BC 都使用当前事务,那么虽然 ABC 所使用的事务资源是一样的,但是只有 A 的 TransactionStatus 是新事务,BC 并不是;那么在 BC 提交事务时,就不会真正的调用提交,只有回到 A 执行 commit 操作时,才会真正的调用提交操作。
这里再解释下,为什么新事务才需要提交,而已经有事务却什么都不用做:
因为对于新事务来说,这里的提交操作已经是事务完成了;而对于非新事务的场景,前置事务(即当前事务)还没有执行完,可能后面还有其他数据库操作,所以这个提交的操作得让当前事务创建方去做,这里并不能提交。
回滚事务
除了提交,还有回滚呢,回滚事务的逻辑和提交事务类似:
-
如果是新事务才回滚,原因上面已经介绍过了
-
如果不是新事务则只设置回滚标记
-
处理挂起资源
注意:事务管理器是不包含回滚策略这个东西的,回滚策略是 AOP 版的事务管理增强的功能,但这个功能并不属于核心的事务管理器
自动挡与手动挡
Spring 的事务管理功能都是围绕着上面这个事务管理器运行的,提供了三种管理事务的方式,分别是:
-
XML AOP 的事务管理 - 比较古老现在用的不多
-
注解版本的事务管理 - @Transactional
-
TransactionTemplate - 手动挡的事务管理,也称编程式事务管理
自动挡
XML/@Transactional 两种基于 AOP 的注解管理,其入口类是 TransactionInterceptor,是一个 AOP 的 Interceptor,负责调用事务管理器来实现事务管理。
因为核心功能都在事务管理器里实现,所以这个 AOP Interceptor 很简单,只是调用一下事务管理器,核心(伪)代码如下:
public Object invoke(MethodInvocation invocation) throws Throwable {// 获取事务资源Object transaction = transactionManager.getTransaction(txAttr); Object retVal;try {// 执行业务代码retVal = invocation.proceedWithInvocation();// 提交事务transactionManager.commit(txStatus);} catch (Throwable ex){// 先判断异常回滚策略,然后调用事务管理器的 rollbackrollbackOn(ex, txStatus);}
}
并且 AOP 这种自动挡的事务管理还增加了一个回滚策略的玩法,这个是手动挡 TransactionTemplate 所没有的,但这个功能并不在事务管理器中,只是 AOP 版事务的一个增强。
手动挡
TransactionTemplate
这个是手动挡的事务管理,虽然没有注解的方便,但是好在灵活,异常/回滚啥的都可以自己控制。
所以这个实现更简单,连异常回滚策略都没有,特殊的回滚方式还要自己设置(默认是任何异常都会回滚),核心(伪)代码如下:
public <T> T execute(TransactionCallback<T> action) throws TransactionException {// 获取事务资源TransactionStatus status = this.transactionManager.getTransaction(this);T result;try {// 执行 callback 业务代码result = action.doInTransaction(status);}catch (Throwable ex) {// 调用事务管理器的 rollbackrollbackOnException(status, ex);}提交事务this.transactionManager.commit(status);}
}
为什么有这么方便的自动挡,还要手动挡?
因为手动挡更灵活啊,想怎么玩就怎么玩,比如我可以在一个方法中,执行多个数据库操作,但使用不同的事务资源:
Integer rows = new TransactionTemplate((PlatformTransactionManager) transactionManager,new DefaultTransactionDefinition(TransactionDefinition.ISOLATION_READ_UNCOMMITTED)).execute(new TransactionCallback<Integer>() {@Overridepublic Integer doInTransaction(TransactionStatus status) {// update 0int rows0 = jdbcTemplate.update(...);// update 1int rows1 = jdbcTemplate.update(...);return rows0 + rows1;}});Integer rows2 = new TransactionTemplate((PlatformTransactionManager) transactionManager,new DefaultTransactionDefinition(TransactionDefinition.ISOLATION_READ_UNCOMMITTED)).execute(new TransactionCallback<Integer>() {@Overridepublic Integer doInTransaction(TransactionStatus status) {// update 2int rows2 = jdbcTemplate.update(...);return rows2;}});
在上面这个例子里,通过 TransactionTemplate 我们可以精确的控制 update0/update1 使用同一个事务资源和隔离级别,而 update2 单独使用一个事务资源,并且不需要新建类加注解的方式。
手自一体可以吗?
当然可以,只要我们使用的是同一个事务管理器的实例,因为绑定资源到同步资源管理器这个操作是在事务管理器中进行的。
AOP 版本的事务管理里,同样可以使用手动挡的事务管理继续操作,而且还可以使用同一个事务资源 。
比如下面这段代码,update1/update2 仍然在一个事务内,并且 update2 的 callback 结束后并不会提交事务,事务最终会在 methodA 结束时,TransactionInterceptor 中才会提交
@Transactional
public void methodA(){// update 1jdbcTemplate.update(...);new TransactionTemplate((PlatformTransactionManager) transactionManager,new DefaultTransactionDefinition(TransactionDefinition.ISOLATION_READ_UNCOMMITTED)).execute(new TransactionCallback<Integer>() {@Overridepublic Integer doInTransaction(TransactionStatus status) {// update 2int rows2 = jdbcTemplate.update(...);return rows2;}});}
总结
Spring 的事务管理,其核心是一个抽象的事务管理器,XML/@Transactional/TransactionTemplate 几种方式都是基于这个事务管理器的,三中方式的核心实现区别并不大,只是入口不同而已。
本文为了方便理解,省略了大量的非关键实现细节,可能会导致有部分描述不严谨的地方,如有问题欢迎评论区留言。
作者:京东保险 蒋信
来源:京东云开发者社区 转载请注明来源
相关文章:
从头到尾说一次 Spring 事务管理(器) | 京东云技术团队
事务管理,一个被说烂的也被看烂的话题,还是八股文中的基础股之一。 本文会从设计角度,一步步的剖析 Spring 事务管理的设计思路(都会设计事务管理器了,还能玩不转?) 为什么需要事务管理&…...
php 系列题目,包含查看后端源代码
一、弱类型比较问题 原则: 1.字符串和数字比较,字符串回被转换成数字。 "admin" 0(true) admin被转换成数字,由于admin是字符串,转换失败,变成0 int(admin)0,所以比较结果是ture 2.混合字符串转…...
令牌桶C语言代码实现
令牌桶实例 令牌桶三要素 cps 每秒钟传输字节数 burst 令牌桶内最多能传输的字节数,token的最大值 token 令牌的个数 之前是一个令牌(token)对应一个字节,现在将一个token变为一个cps,cps是解码速率,每攒到一个令牌ÿ…...
Mybatis 建立依赖失败:报错Dependency ‘mysql:mysql-connector-java:8.0.28‘ not found
Mybatis 建立依赖失败:报错Dependency ‘mysql:mysql-connector-java:8.0.28’ not found 解决办法: 写完依赖代码,直接重构,下载依赖。 图片: ![Alt](https://img-home.csdnimg.cn/images/20220524100510.png Mac 版本注意Ide…...
多线程+隧道代理:提升爬虫速度
在进行大规模数据爬取时,爬虫速度往往是一个关键问题。本文将介绍一个提升爬虫速度的秘密武器:多线程隧道代理。通过合理地利用多线程技术和使用隧道代理,我们可以显著提高爬虫的效率和稳定性。本文将为你提供详细的解决方案和实际操作价值&a…...
使用@Configuration和@Bean给spring容器中注入组件
Confguration->告诉spring这是一个配置类 以前我们是使用配置文件来注册bean的,现如今可以用Configuration 来代替配置文件。 //配置配配置文件 Configuration // 告诉Spring这是一个配置类,等同于以前的配置文件 public class MainConfig {// Bean注解是给IOC…...
信号波形解读
can波形解读 实际波形 标准帧 发送数据 仲裁段 0x1AA 数据长度为8字节 内容为:0x41, 0x20, 0x38, 0x41, 0x00, 0x16, 0x00, 0x00 波特率 111K...
Centos 解决 XXX不在 sudoers 文件中。此事将被报告。的错误
本来想使用 sudo 拷贝一个文件,结果出现上面的问题! 下面是解决方法: 首先登录root,然后执行下面的命令 vim /etc/sudoers 将你需要添加的用户带红色框线的地方,模仿root写一遍,然后保存! …...
雪花算法和uuid的区别
雪花算法(Snowflake Algorithm)和 UUID(Universally Unique Identifier)都是用于生成唯一标识符的方法,但它们在实现和适用场景上存在一些区别。 雪花算法: 雪花算法是Twitter开发的一种分布式ID生成算法…...
docker之DockerFile与网络
目录 DockerFile 构建步骤 基础知识 指令 实战:构建自己的centos 第一步:编写dockerfile文件 第二步:构建镜像文件 docker网络 原理 功能 网络模式 host模式 container模式 none模式 bridge模式 DockerFile dockerfile 是用来…...
知识蒸馏开山之作(部分解读)—Distilling the Knowledge in a Neural Network
1、蒸馏温度T 正常的模型学习到的就是在正确的类别上得到最大的概率,但是不正确的分类上也会得到一些概率尽管有时这些概率很小,但是在这些不正确的分类中,有一些分类的可能性仍然是其他类别的很多倍。但是对这些非正确类别的预测概率也能反…...
centos 7 安装 docker-compose curl 设置代理
sudo curl -x “http://192.168.1.2:3128” 需要验证的代理 sudo curl -x “http://username:password192.168.1.2:3128” 1.下载 sudo curl -L "https://github.com/docker/compose/releases/download/1.23.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/lo…...
3D姿态相关的损失函数
loss_mpjpe: 计算预测3D关键点与真值之间的平均距离误差(MPJPE)。 loss_n_mpjpe: 计算去除尺度后预测3D关键点误差(N-MPJPE),评估结构误差。 loss_velocity: 计算3D关键点的速度/移动的误差,评估运动的平滑程度。 loss_limb_var: 计算肢体长度的方差,引导生成合理的肢体长度…...
ChatGPT取代人类仍然是空想?有没有一种可能是AI在迷惑人类
ChatGPT自从去年发布以来,就掀起了这些大语言模型将如何颠覆一切的激烈讨论,从为学生写作文、输出SEO文章,甚至取代谷歌成为世界上最受欢迎的搜索引擎,影响领域无所不包,甚至可能取代编剧、小说家和音乐家等从事创意工…...
基于swing的旅游管理系统java jsp旅行团信息mysql源代码
本项目为前几天收费帮学妹做的一个项目,Java EE JSP项目,在工作环境中基本使用不到,但是很多学校把这个当作编程入门的项目来做,故分享出本项目供初学者参考。 一、项目描述 基于swing的旅游管理系统 系统有1权限:管…...
Windows wsl2支持systemd
背景 很多Linux发行版都是使用systemd来管理程序进程,但是在WSL中默认是用init来管理进程的。 为了符合长久的使用习惯,且省去不必要的学习成本,就在WSL的发行版(我这里安装的是Ubuntu20.04)中支持systemd࿰…...
NLP - 如何解决ModuleNotFoundError: No module named ‘jieba‘的问题
错误描述 在JUPYTER中,使用结巴分词,出错: ModuleNotFoundError: No module named jieba解决方案 在 Anaconda Prompt 中,执行以下指令(可以解决): pip install jieba -i https://pypi.tuna…...
Windows10上VS2022单步调试FFmpeg 4.2源码
之前在 https://blog.csdn.net/fengbingchun/article/details/103735560 介绍过通过VS2017单步调试FFmpeg源码的方法,这里在Windows10上通过VS2022单步调试FFmpeg 4.2的方法:基于GitHub上ShiftMediaProject/FFmpeg项目,下面对编译过程进行说明…...
【tkinter 专栏】菜单组件
文章目录 前言本章内容导图1. Menu 菜单组件Menu 组件的基本使用制作二级下拉菜单为菜单添加快捷键2. Treeview 树形菜单组件Treeview 组件的基本使用菜单项的获取与编辑前言 本专栏将参考《Python GUI 设计 tkinter 从入门到实践》书籍(吉林大学出版社 ISBN: 9787569275001)…...
【LeetCode-经典面试150题-day10】
目录 242.有效的字母异位词 49.字母异位词分组 202.快乐数 219.存在重复元素Ⅱ 383.赎金信 205.同构字符串 290.单词规律 242.有效的字母异位词 题意: 给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。 注意:若 s 和…...
Transformer在医学影像中的应用综述-分类
文章目录 COVID-19 Diagnosis黑盒模型可解释的模型 肿瘤分类黑盒模型可解释模型 视网膜疾病分类小结 总体结构 COVID-19 Diagnosis 黑盒模型 Point-of-Care Transformer(POCFormer):利用Linformer将自注意的空间和时间复杂度从二次型降低到线性型。POCFormer有200…...
新服务器基本环境下载conda + docker + docker-compose + git
文章目录 Ubuntu 允许root用户登录 centos无所谓condadockerubuntucentos docker-compose官方下载docker-compose国内镜像 gitUbuntuCentos Ubuntu 允许root用户登录 centos无所谓 # 以普通用户登录系统,创建root用户的密码 sudo passwd root# SSH 放行 sudo sed -…...
Matlab论文插图绘制模板第108期—特征渲染的标签散点图
在之前的文章中,分享了Matlab标签散点图的绘制模板: 进一步,再来分享一下特征渲染的标签散点图的绘制模板,以便再添加一个维度的信息。 先来看一下成品效果: 特别提示:本期内容『数据代码』已上传资源群中…...
设计模式之中介者模式(Mediator)的C++实现
1、中介者模式的提出 在软件组件开发过程中,如果存在多个对象,且这些对象之间存在的相互交互的情况不是一一对应的情况,这种功能组件间的对象引用关系比较复杂,耦合度较高。如果有一些新的需求变化,则不易扩展。中介者…...
css弹性布局的方式
概述 任何一个容器都可以定义为弹性布局容器,使用display:flex(display:inline-flex)开启弹性布局。 2个方向轴:水平主轴和垂直交叉轴 6个容器属性 1.flex-direction :主轴的方向 2.justify-content:子元素在主轴的对齐方式 …...
阿里云源 Python、npm、git、goproxy
阿里云源 Python、npm、git、goproxy 各种设置源的方式也都比较常见,但是根本记不住,每次都查感觉也不太好。 正好发现了个宝藏地址,看起来还挺全的,以后找源也可以先在这个地方翻翻,顺便就搞了几个放到一个脚本里边…...
微服务架构1.0
微服务架构 微服务架构是一种应用程序架构模式,将一个应用程序划分为一组小型、独立、自治的服务,每个服务专注于一个特定的业务功能。 每个微服务都可以独立开发、部署、扩展和维护,通过定义良好的接口和协议,它们可以相互通信…...
iOS开发Swift-基础部分
1.常量 let maxNum 10 //单个常量赋值 let maxNum 10, minNum 2 //多个常量赋值用逗号隔开2.变量 var x 0.0 //单个变量赋值 var x 0.0, y 0.1 //多个变量赋值用逗号隔开3.类型注解 系统可通过赋初始值进行自动推断。 var name&#…...
【LeetCode-经典面试150题-day11】
目录 128.最长连续序列 228.汇总区间 56.合并区间 57.插入区间 452.用最少数量的箭引爆气球 128.最长连续序列 题意: 给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。 请你设计并…...
深度学习入门(三):卷积神经网络(CNN)
引入 给定一张图片,计算机需要模型判断图里的东西是什么? (car、truck、airplane、ship、horse) 一、卷积神经网络整体架构 CONV:卷积计算层,线性乘积求和RELU:激励层,激活函数P…...
深圳微信网站开发/百度网盘客服人工电话
最近在搞容器的监控,遇到influxdb这个库,搞了两天,些许明白了些套路,做个记录,备忘.... 小结如下: influxdb go语言编写 默认情况influxdb创建的库关联autogen的RP(存储策略),即数据会保留永久 监控和日志的区别 最近搞监控,所谓监控就是监控服务肉体是否健康(还活着/生病? 各…...
做游戏网站的分析/给公司做网站的公司
MySQL数据库基本操作(增删改查)进入MySQL:(前提是安装了MySQL或者集成了MySQL的软件包并且开启了MySQL服务)– Mysql –u 用户名 –p //回车– 输入密码 //正确则直接进入mysql注意:所有的sql语句末尾都要分号,sql语句的大…...
在车子男女做的视频网站/百度指数官网入口
原文:jakearchibald.com/2015/tasks-… 译者:前端小智 为了保证的可读性,本文采用意译而非直译。 想阅读更多优质文章请猛戳GitHub博客,一年百来篇优质文章等着你! 思考下面 JavaScript 代码: console.log(script star…...
温州微网站制作电话/武汉网站seo公司
关于JavaScript中this指向问题 js中,this在不同情况下指向对象很复杂,分成很多种情况。 this指向window情况 注意:所有this指向window的情况,在严格模式下,this值为undefined 严格模式下,apply(),call()…...
政府网站栏目建设反馈意见/直销怎么做才最快成功
axios 安装 npm install axios React中配置代理解决跨域问题 方法一 在package.json中追加如下配置 "proxy":"http://localhost:5000"说明: 优点:配置简单,前端请求资源时可以不加任何前缀。缺点:不能配…...
华艺网站建设/百度云网盘入口
Python是目前最火的编程语言之一,python简单易学、好上手,是很多人的首选编程语言。对于想做程序员的人来说,学python能够更快地接触到计算机工作。对于其他行业的人而言,学好了python也能大大提高工作效率。Python学了有什么好处…...