深入理解Spring事务
目录
- 什么是Spring事务
- 为什么需要Spring事务
- Spring事务的实现
- Spring事务的传播机制
- Spring事务的底层原理
- @EnableTransactionManagement --开启Spring管理事务
- @Import(TransactionManagementConfigurationSelector.class) --提供两个bean
- AutoProxyRegistrar --启用AOP的功能,创建一个bean后置处理器,会拦截所有bean的创建,对符合条件的bean创建代理。
- ProxyTransactionManagementConfiguration --添加事务事务拦截器:TransactionInterceptor
- TransactionInterceptor --负责拦截@Transaction方法的执行,在方法执行之前开启spring事务,方法执行完毕之后提交或者回滚事务
- invokeWithinTransaction --事务拦截器的入口
- determineTransactionManager --获取事务管理器
- completeTransactionAfterThrowing --异常如何处理
- Spring事务失效的场景
- 扩展:多数据源的事务管理
什么是Spring事务
事务其实是一个并发控制单位,是用户定义的一个操作序列,这些操作要么全部完成,要不全部不完成,是一个不可分割的工作单位。事务有 ACID 四个特性,即:
- 原子性(Atomicity):事务中的所有操作,或者全部完成,或者全部不完成,不会结束在中间某个环节。
- 一致性(Consistency):在事务开始之前和事务结束以后,数据库的完整性没有被破坏。
- 隔离性(Isolation):多个事务之间是独立的,不相互影响的。
- 持久性(Durability):事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
而我们说的 Spring 事务,其实是事务在 Spring 中的实现。
为什么需要Spring事务
为了解释清楚这个问题,我们举个简单的例子:银行里树哥要给小黑转 1000 块钱,这时候会有两个必要的操作:
- 将树哥的账户余额减少 1000 元。
- 将小黑的账户余额增加 1000 元。
这两个操作,要么一起都完成,要么都不完成。如果其中某个成功,另外一个失败,那么就会出现严重的问题。而我们要保证这个操作的原子性,就必须通过 Spring 事务来完成,这就是 Spring 事务存在的原因。
如果你深入了解过 MySQL 事务,那么你应该知道:MySQL 默认情况下,对于所有的单条语句都作为一个单独的事务来执行。我们要使用 MySQL 事务的时候,可以通过手动提交事务来控制事务范围。Spring 事务的本质,其实就是通过 Spring AOP 切面技术,在合适的地方开启事务,接着在合适的地方提交事务或回滚事务,从而实现了业务编程层面的事务操作。
Spring事务的实现
在此之前需要提前讲解一下下面三个接口:
- TransactionDefinition :定义一些基本的事务属性
public interface TransactionDefinition {int PROPAGATION_REQUIRED = 0;int PROPAGATION_SUPPORTS = 1;int PROPAGATION_MANDATORY = 2;int PROPAGATION_REQUIRES_NEW = 3;int PROPAGATION_NOT_SUPPORTED = 4;int PROPAGATION_NEVER = 5;int PROPAGATION_NESTED = 6;int ISOLATION_DEFAULT = -1;int ISOLATION_READ_UNCOMMITTED = 1;int ISOLATION_READ_COMMITTED = 2;int ISOLATION_REPEATABLE_READ = 4;int ISOLATION_SERIALIZABLE = 8;int TIMEOUT_DEFAULT = -1;// 返回事务的传播行为,默认值为 REQUIRED。int getPropagationBehavior(); // 返回事务的隔离级别,默认值是 DEFAULTint getIsolationLevel(); // 返回事务的超时时间,默认值为-1。如果超过该时间限制但事务还没有完成,则自动回滚事务。int getTimeout(); // 返回是否为只读事务,默认值为 falseboolean isReadOnly();@NullableString getName(); } - PlatformTransactionManager:事务管理器接口,Spring 为各个平台如:JDBC、MyBatis(DataSourceTransactionManager)、Hibernate(HibernateTransactionManager)、JPA(JpaTransactionManager)等都提供了对应的事务管理器,但是具体的实现就是各个平台自己的事情了。
public interface PlatformTransactionManager {// 获得事务TransactionStatus getTransaction(@Nullable TransactionDefinition var1) throws TransactionException;// 提交事务void commit(TransactionStatus var1) throws TransactionException;// 回滚事务void rollback(TransactionStatus var1) throws TransactionException; } - TransactionStatus:接口用来记录事务的状态,该接口定义了一组方法,用来获取或判断事务的相应状态信息
public interface TransactionStatus{// 是否是新的事务boolean isNewTransaction();// 是否有恢复点boolean hasSavepoint();// 设置为只回滚void setRollbackOnly();// 是否为只回滚boolean isRollbackOnly();// 是否已完成boolean isCompleted; }
回归正文:Spring事务的实现方式主要有两种:
①编程式事务:这种方式通过编码的方式直接管理事务,通常是使用PlatformTransactionManager接口的实现,通过手动的开启事务、关闭事务以及回滚事务。编程式事务管理提供了最细粒度的控制,但代码复杂性较高,且容易出错。示例代码:
@Service
public class AccountService {private AccountMapper accountmapper;//事务管理器private PlatformTransactionManager transactionManager;//构造器方式注入public AccountService(AccountMapper accountmapper, PlatformTransactionManager transactionManager) {this.accountmapper = accountmapper;this.transactionManager = transactionManager;}//转账方法,使用编程式事务管理。public void transferMoneyProgrammatic(Long fromAccountId, Long toAccountId, BigDecimal amount) {//TransactionDefinition:定义一些基本的事务属性TransactionDefinition definition = new DefaultTransactionDefinition();definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);//通过事务管理器TransactionManager获取到一个事务,TransactionStatus包含了事务的当前状态和一些其他与事务相关的信息//在这里开启了事务TransactionStatus status = transactionManager.getTransaction(definition);try {//执行业务逻辑Account fromAccount = accountmapper.findById(fromAccountId).orElseThrow();Account toAccount = accountmapper.findById(toAccountId).orElseThrow();fromAccount.setBalance(fromAccount.getBalance().subtract(amount));toAccount.setBalance(toAccount.getBalance().add(amount));accountmapper.save(fromAccount);accountmapper.save(toAccount);//提交事务transactionManager.commit(status);} catch (Exception e) {//回滚事务transactionManager.rollback(status);throw e;}}
}
②声明式事务:这是Spring推荐的方式,通过在方法上使用@Transactional注解来管理事务。声明式事务管理使得事务管理变得声明化,代码更加简洁,且减少了出错的可能性。@Transactional注解可以指定传播行为、隔离级别等事务属性,从而控制事务的行为 。示例代码:
@Service
public class AccountService {private AccountMapper accountmapper;//构造器方式注入public AccountService(AccountMapper accountmapper) {this.accountmapper = accountmapper;}//转账方法,使用声明式式事务管理。@Transactionalpublic void transferMoneyProgrammatic(Long fromAccountId, Long toAccountId, BigDecimal amount) {//执行业务逻辑Account fromAccount = accountmapper.findById(fromAccountId).orElseThrow();Account toAccount = accountmapper.findById(toAccountId).orElseThrow();fromAccount.setBalance(fromAccount.getBalance().subtract(amount));toAccount.setBalance(toAccount.getBalance().add(amount));accountmapper.save(fromAccount);accountmapper.save(toAccount);}
}
Spring事务的传播机制
事务的传播行为用来描述:系统中的一些方法交由spring来管理事务,当这些方法之间出现嵌套调用的时候,事务所表现出来的行为是什么样的,例如:

Service1中的m1方法和Service2中的m2方法上面都有@Transactional注解,说明这2个方法由spring来控制事务。但是注意m1中2行代码,先执行了一个insert,然后调用service2中的m2方法,service2中的m2方法也执行了一个insert。
那么大家觉得这2个insert会在一个事务中运行么?也就是说此时事务的表现行为是什么样的呢?这个就是spring事务的传播行为来控制的事情,不同的传播行为,表现会不一样,可能他们会在一个事务中执行,也可能不会在一个事务中执行,这就需要看传播行为的配置了。

注意:这7种传播行为有个前提,他们的事务管理器是同一个的时候,才会有上面描述中的表现行为!!!!!!!!
推荐去看这篇文章,讲述了事务传播行为之间的各种搭配情况分析
Spring事务的底层原理
我们知道声明式事务的实现是通过通过aop的功能,通过拦截器拦截 @Transaction 方法,在方法前后添加事务功能。具体步骤:①第一步在启动类上加上@EnableTransactionmanagement注解 ②第二步在目标方法上加上@Transaction注解即可实现

@EnableTransactionManagement --开启Spring管理事务
@EnableTransactionManagement注解会开启spring自动管理事务的功能,有了这个注解之后,spring容器启动的过程中,会拦截所有bean的创建过程,判断bean 是否需要让spring来管理事务,即判断bean中是否有@Transaction注解,如果找到就会被spring容器通过aop的方式创建代理,代理中会添加一个拦截器,通过拦截器在方法前后添加事务的功能
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class) //核心
public @interface EnableTransactionManagement {// 是基于类的代理(cglib),还是基于接口的代理(jdk动态代理),默认为false,表示是基于jdk动态代理boolean proxyTargetClass() default false;//通知的模式,默认是通过aop的方式AdviceMode mode() default AdviceMode.PROXY;// 我们知道这个注解的功能最终是通过aop的方式来实现的,对bean创建了一个代理,代理中添加了一个拦截器// 当代理中还有其他拦截器的是时候,可以通过order这个属性来指定事务拦截器的顺序// 默认值是 LOWEST_PRECEDENCE = Integer.MAX_VALUE,拦截器的执行顺序是order升序int order() default Ordered.LOWEST_PRECEDENCE;
}
@Import(TransactionManagementConfigurationSelector.class) --提供两个bean
public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {//这个方法会返回一个类名数组,spring容器启动过程中会自动调用这个方法,//将这个方法指定的类注册到spring容器中;方法的参数是AdviceMode,这个就是 //@EnableTransactionManagement注解中mode属性的值,默认是PROXYprotected String[] selectImports(AdviceMode adviceMode) {switch (adviceMode) {case PROXY: <-----//默认会走这里return new String[]{AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName()};case ASPECTJ:return new String[]{this.determineTransactionAspectClass()};default:return null;}}
}
最终会在Spring中注册下面两个bean:
AutoProxyRegistrar
ProxyTransactionManagementConfiguration
AutoProxyRegistrar --启用AOP的功能,创建一个bean后置处理器,会拦截所有bean的创建,对符合条件的bean创建代理。
public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar {public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {boolean candidateFound = false;Set<String> annTypes = importingClassMetadata.getAnnotationTypes();Iterator var5 = annTypes.iterator();while(var5.hasNext()) {String annType = (String)var5.next();AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);if (candidate != null) {Object mode = candidate.get("mode");Object proxyTargetClass = candidate.get("proxyTargetClass");if (mode != null && proxyTargetClass != null && AdviceMode.class == mode.getClass() && Boolean.class == proxyTargetClass.getClass()) {candidateFound = true;if (mode == AdviceMode.PROXY) {//这个代码的作用就是在容器中做了一个非常关键的bean:InfrastructureAdvisorAutoProxyCreator,//这个类是bean后置处理器,会拦截所有bean的创建,对符合条件的bean创建代理。AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);if ((Boolean)proxyTargetClass) {AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);return;}}}}}
}
ProxyTransactionManagementConfiguration --添加事务事务拦截器:TransactionInterceptor
//部分源码
@Configuration(proxyBeanMethods = false)
@Role(2)
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {@Bean@Role(2)public TransactionInterceptor transactionInterceptor(TransactionAttributeSource transactionAttributeSource) {TransactionInterceptor interceptor = new TransactionInterceptor();interceptor.setTransactionAttributeSource(transactionAttributeSource);if (this.txManager != null) {interceptor.setTransactionManager(this.txManager);}return interceptor;}
}
二者结合起来的效果就是:对@Transaction标注的bean创建代理对象,代理对象中通过TransactionInterceptor拦截器来实现事务管理的功能。
TransactionInterceptor --负责拦截@Transaction方法的执行,在方法执行之前开启spring事务,方法执行完毕之后提交或者回滚事务
//部分源码
public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable {@Nullablepublic Object invoke(MethodInvocation invocation) throws Throwable {Class<?> targetClass = invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null;Method var10001 = invocation.getMethod();invocation.getClass();return this.invokeWithinTransaction(var10001, targetClass, invocation::proceed);}private void writeObject(ObjectOutputStream oos) throws IOException {oos.defaultWriteObject();oos.writeObject(this.getTransactionManagerBeanName());oos.writeObject(this.getTransactionManager());oos.writeObject(this.getTransactionAttributeSource());oos.writeObject(this.getBeanFactory());}private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {ois.defaultReadObject();this.setTransactionManagerBeanName((String)ois.readObject());this.setTransactionManager((PlatformTransactionManager)ois.readObject());this.setTransactionAttributeSource((TransactionAttributeSource)ois.readObject());this.setBeanFactory((BeanFactory)ois.readObject());}
}
invokeWithinTransaction --事务拦截器的入口
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,final InvocationCallback invocation) throws Throwable {TransactionAttributeSource tas = getTransactionAttributeSource();//@6-1:获取事务属性配置信息:通过TransactionAttributeSource.getTransactionAttribute解析@Trasaction注解得到事务属性配置信息final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);//@6-2:获取事务管理器final TransactionManager tm = determineTransactionManager(txAttr);//将事务管理器tx转换为 PlatformTransactionManagerPlatformTransactionManager ptm = asPlatformTransactionManager(tm);if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {// createTransactionIfNecessary内部,这里就不说了,内部主要就是使用spring事务硬编码的方式开启事务,最终会返回一个TransactionInfo对象TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);// 业务方法返回值Object retVal;try {//调用aop中的下一个拦截器,最终会调用到业务目标方法,获取到目标方法的返回值retVal = invocation.proceedWithInvocation();}catch (Throwable ex) {//6-3:异常情况下,如何走?可能只需提交,也可能只需回滚,这个取决于事务的配置completeTransactionAfterThrowing(txInfo, ex);throw ex;}finally {//清理事务信息cleanupTransactionInfo(txInfo);}//6-4:业务方法返回之后,只需事务提交操作commitTransactionAfterReturning(txInfo);//返回执行结果return retVal;}
}
这个方法的执行流程非常像我们编程式事务的实现方式
1、定义事务属性信息:TransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
2、定义事务管理器:PlatformTransactionManager platformTransactionManager = new DataSourceTransactionManager(dataSource);
3、获取事务:TransactionStatus transactionStatus = platformTransactionManager.getTransaction(transactionDefinition);
4、执行sql操作:比如上面通过JdbcTemplate的各种方法执行各种sql操作
5、提交事务(platformTransactionManager.commit)或者回滚事务(platformTransactionManager.rollback)
determineTransactionManager --获取事务管理器
我们再具体来看:首先是如何通过determineTransactionManager()方法获取事务管理器的:
@Nullable
protected TransactionManager determineTransactionManager(@Nullable TransactionAttribute txAttr) {if (txAttr != null && this.beanFactory != null) {String qualifier = txAttr.getQualifier();if (StringUtils.hasText(qualifier)) {return this.determineQualifiedTransactionManager(this.beanFactory, qualifier);} else if (StringUtils.hasText(this.transactionManagerBeanName)) {return this.determineQualifiedTransactionManager(this.beanFactory, this.transactionManagerBeanName);} else {TransactionManager defaultTransactionManager = this.getTransactionManager();if (defaultTransactionManager == null) {defaultTransactionManager = (TransactionManager)this.transactionManagerCache.get(DEFAULT_TRANSACTION_MANAGER_KEY);if (defaultTransactionManager == null) {defaultTransactionManager = (TransactionManager)this.beanFactory.getBean(TransactionManager.class);this.transactionManagerCache.putIfAbsent(DEFAULT_TRANSACTION_MANAGER_KEY, defaultTransactionManager);}}return defaultTransactionManager;}} else {return this.getTransactionManager();}
从上面可知,事务管理器的查找顺序:
1、先看@Transactional中是否通过value或者transactionManager指定了事务管理器
2、TransactionInterceptor.transactionManagerBeanName(即我们在TransactionInterceptor类中通过构造方法传进来的事务管理器)是否有值,如果有,将通过这个值查找事务管理器
3、如果上面2种都没有,将从spring容器中查找TransactionManager类型的事务管理器
completeTransactionAfterThrowing --异常如何处理
然后再来看completeTransactionAfterThrowing()方法中出现异常该如何处理
protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {if (txInfo != null && txInfo.getTransactionStatus() != null) {//@6-3-1:判断事务是否需要回滚if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {//通过事务管理器回滚事务txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());}else {//通过事务管理器提交事务txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());}}
}//跟着进入rollbackOn方法
public boolean rollbackOn(Throwable ex) {RollbackRuleAttribute winner = null;int deepest = Integer.MAX_VALUE;//@Trasaction中可以通过rollbackFor指定需要回滚的异常列表,通过noRollbackFor属性指定不需要回滚的异常//根据@Transactional中指定的回滚规则判断ex类型的异常是否需要回滚if (this.rollbackRules != null) {for (RollbackRuleAttribute rule : this.rollbackRules) {int depth = rule.getDepth(ex);if (depth >= 0 && depth < deepest) {deepest = depth;winner = rule;}}}//若@Transactional注解中没有匹配到,这走默认的规则,将通过super.rollbackOn来判断if (winner == null) {return super.rollbackOn(ex);}return !(winner instanceof NoRollbackRuleAttribute);
}//跟着进入rollbackOn方法
@Override
public boolean rollbackOn(Throwable ex) {return (ex instanceof RuntimeException || ex instanceof Error);
}
根据上面的源码分析,可以看出默认情况下,异常类型是RuntimeException或者Error的情况下,事务才会回滚。 @Trasaction中可以通过rollbackFor指定需要回滚的异常列表,通过noRollbackFor属性指定不需要回滚的异常。
源码干货暂时分析到这,已经干的不行了
这里推荐看一篇非常详细且为参考的文章:@Transaction源码深度解析
Spring事务失效的场景
扩展:多数据源的事务管理
相关文章:
深入理解Spring事务
目录 什么是Spring事务为什么需要Spring事务Spring事务的实现 Spring事务的传播机制Spring事务的底层原理 EnableTransactionManagement --开启Spring管理事务Import(TransactionManagementConfigurationSelector.class) --提供两个beanAutoProxyRegistrar --启用AOP的功能&am…...
Ubuntu22.04深度学习环境安装【Anaconda+Pycharm】
anaconda可以提供多个独立的虚拟环境,方便我们学习深度学习(比如复现论文); Pycharm编辑器可以高效的编写python代码,也是一个很不错的工具。 下面就记录下Ubuntu22.04的安装流程: 1.Anaconda安装 下载Ana…...
五、docker的网络模式
五、docker的网络模式 5.1 Docker的四种网络模式 当你安装docker时,它会自动创建三个网络,可使用如下命令查看: [rootlocalhost ~]# docker network ls NETWORK ID NAME DRIVER SCOPE 7390284b02d6 bridge bridge lo…...
使用el-row和el-col混合table设计栅格化,实现表头自适应宽度,表格高度占位
演示效果: 如上图,由于地址信息很长,需要占多个格子,所以需要错开,若想实现这种混合效果,可以这样搭建: 页面效果: 代码分析: 上面使用el-row和el-col搭建表单显示 第一排三个8,第二排8和16 下面混合table实现,并使用border来自适应宽度…...
【服务器监控】grafana+Prometheus+node exporter详细部署文档
我们在进行测试时,不可能一直手动看着服务器的性能消耗,这时候就需要有个工具替我们监控服务器的性能消耗。这里记录下grafanaPrometheusnodeExporter的组合用于监控服务器。 简单介绍: grafana:看板工具,所有采集的…...
JavaScript中todolist操作--待办事项的添加 删除 完成功能
效果图 在文本框中输入内容点击添加按钮会在下面生成 添加功能 html <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0&qu…...
Windows中MySQL8.3.4 MSI版本——详细安装教程
一、下载MySQL安装文件。 下载地址:MySQL官网 进入后点击下面的MySQL社区版下载 点击MySQL Comunity Server。 我这里选择的是版本8.4.3LTS版本,在线对应的msi文件。 点击No thanks,直接下载。 二、安装MySQL 2.1、双击刚刚下载好的msi文件,…...
MySQL-DDL之数据库操作
文章目录 一. 创建数据库1. 直接创建数据库,如果存在则报错2. 如果数据库不存在则创建3. 创建数据库时设置字符集4. 栗子 二. 查看数据库1. 查看数据库 三. 删除数据库1. 删除数据库 四. 使用数据库1. 使用数据库2. 查看正在使用的数据库 数据定义语言:简…...
Python 笔记之进程通信
当需要创建的子进程数量不多时,可以直接利用multiprocessing中的Process动态生成多个进程 但是如果是上百个或者上千个目标,手动去创建进程的工作量很大,此时就可以利用到Multiprocessing模块提供的Pool方法 初始化pool时,可以指定…...
【Transformer序列预测】Pytorch中构建Transformer对序列进行预测源代码
Python,Pytorch中构建Transformer进行序列预测源程序。包含所有的源代码和数据,程序能够一键运行。此程序是完整的Transformer,即使用了Encoder、Decoder和Embedding所有模块。源程序是用jupyterLab所写,建议分块运行。也整理了.p…...
生产者-消费者模式:多线程并发协作的经典案例
生产者-消费者模式是多线程并发编程中一个非常经典的模式,它通过解耦生产者和消费者的关系,使得两者可以独立工作,从而提高系统的并发性和可扩展性。本文将详细介绍生产者-消费者模式的概念、实现方式以及应用场景。 1 生产者-消费者模式概述…...
数据库-mysql(基本语句)
演示工具:navicat 连接:mydb 一.操作数据库 1.创建数据库 ①create database 数据库名称 //普通创建 ②create database if not exists 数据库名称 //创建数据库,判断不存在,再创建: 使用指定数据库 use 数据库…...
android12L super.img 解压缩及其挂载到ubuntu18.04
本文介绍如何在Ubuntu18.04上解压缩高通平台Android12L的super.img,并将其挂载到系统中查看内容。 在源码的根目录下,执行如下命令: out/host/linux-x86/bin/simg2img out/target/product/msmnile_gvmq/super.img super.img_rawmkdir super…...
flask简易版的后端服务创建接口(python)
1.pip install安装Flask和CORS 2.创建http_server.py文件,内容如下 """ ============================ 简易版的后端服务 ============================ """ from flask import Flask, request, jsonify from flask_cors import CORS app = F…...
小程序入门学习(四)之全局配置
一、 全局配置文件及常用的配置项 小程序根目录下的 app.json 文件是小程序的全局配置文件。常用的配置项如下: pages:记录当前小程序所有页面的存放路径 window:全局设置小程序窗口的外观 tabBar:设置小程序底部的 tabBar 效…...
PHP使用RabbitMQ(正常连接与开启SSL验证后的连接)
代码中包含了PHP在一般情况下使用方法和RabbitMQ开启了SSL验证后的使用方法(我这边消费队列是使用接口请求的方式,每次只从中取出一条) 安装amqp扩展 PHP使用RabbitMQ前,需要安装amqp扩展,之前文章中介绍了Windows环…...
轻量级视觉骨干网络 MobileMamba: Lightweight Multi-Receptive Visual Mamba Network
MobileMamba 快速链接解决问题:视觉模型在移动设备端性能和效果的平衡性解决方法:改进网络结构训练和测试策略网络结构改进训练和测试策略 实验支撑:图像分类、分割,目标检测等图像分类结果对比目标检测和实例分割结果对比语义分割…...
科技云报到:数智化转型风高浪急,天翼云如何助力产业踏浪而行?
科技云报到原创。 捷径消亡,破旧立新,是今年千行百业的共同底色。 穿越产业周期,用数字化的力量重塑企业经营与增长的逻辑,再次成为数字化技术应用的主旋律,也是下一阶段产业投资的重点。 随着数字化转型行至“深水区…...
dockerfile部署前后端(vue+springboot)
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言0.环境说明和准备1.前端多环境打包1.1前端多环境设置1.2打包 2.后端项目多环境配置以及打包2.1后端多环境配置2.2项目打包 3.文件上传4.后端镜像制作4.1dockerf…...
c语言的思维导图
之前已经全部学完c语言了,所以为了更好的复习回顾,我做了一份c语言超详细的思维导图,帮助实现一张图就可以复习,避免盲目, 由于平台不支持直接发上图,有想要的小伙伴,可以私信找我要原件...
Java 语言特性(面试系列2)
一、SQL 基础 1. 复杂查询 (1)连接查询(JOIN) 内连接(INNER JOIN):返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...
盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来
一、破局:PCB行业的时代之问 在数字经济蓬勃发展的浪潮中,PCB(印制电路板)作为 “电子产品之母”,其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透,PCB行业面临着前所未有的挑战与机遇。产品迭代…...
【WiFi帧结构】
文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成:MAC头部frame bodyFCS,其中MAC是固定格式的,frame body是可变长度。 MAC头部有frame control,duration,address1,address2,addre…...
可靠性+灵活性:电力载波技术在楼宇自控中的核心价值
可靠性灵活性:电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中,电力载波技术(PLC)凭借其独特的优势,正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据,无需额外布…...
MMaDA: Multimodal Large Diffusion Language Models
CODE : https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA,它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构…...
浅谈不同二分算法的查找情况
二分算法原理比较简单,但是实际的算法模板却有很多,这一切都源于二分查找问题中的复杂情况和二分算法的边界处理,以下是博主对一些二分算法查找的情况分析。 需要说明的是,以下二分算法都是基于有序序列为升序有序的情况…...
libfmt: 现代C++的格式化工具库介绍与酷炫功能
libfmt: 现代C的格式化工具库介绍与酷炫功能 libfmt 是一个开源的C格式化库,提供了高效、安全的文本格式化功能,是C20中引入的std::format的基础实现。它比传统的printf和iostream更安全、更灵活、性能更好。 基本介绍 主要特点 类型安全:…...
c# 局部函数 定义、功能与示例
C# 局部函数:定义、功能与示例 1. 定义与功能 局部函数(Local Function)是嵌套在另一个方法内部的私有方法,仅在包含它的方法内可见。 • 作用:封装仅用于当前方法的逻辑,避免污染类作用域,提升…...
【免费数据】2005-2019年我国272个地级市的旅游竞争力多指标数据(33个指标)
旅游业是一个城市的重要产业构成。旅游竞争力是一个城市竞争力的重要构成部分。一个城市的旅游竞争力反映了其在旅游市场竞争中的比较优势。 今日我们分享的是2005-2019年我国272个地级市的旅游竞争力多指标数据!该数据集源自2025年4月发表于《地理学报》的论文成果…...
ArcPy扩展模块的使用(3)
管理工程项目 arcpy.mp模块允许用户管理布局、地图、报表、文件夹连接、视图等工程项目。例如,可以更新、修复或替换图层数据源,修改图层的符号系统,甚至自动在线执行共享要托管在组织中的工程项。 以下代码展示了如何更新图层的数据源&…...
