深入理解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…...
React学习笔记2-初识React
这篇七个点:1 环境搭建, 2 JSX, 3 组件,4 数据流,5 生命周期,6 React与DOM,7 实例 1 环境搭建 1.1 引用React CDN <!DOCTYPE html> <html lang"en"> <head><met…...
【5G】Spectrum 频谱
频谱是移动运营商的关键资产,可用的频谱是定义移动网络容量和覆盖范围的重要因素。本章讨论了5G的不同频谱选项、它们的特性以及5G早期部署阶段的预期频谱。5G是首个旨在利用大约400 MHz到90 GHz之间所有频段的移动无线系统。5G还设计用于在许可、共享和非许可频谱带…...
解决流网络中不存在s~u~t路径的节点的最大流问题
解决流网络中不存在s~u~t路径的节点的最大流问题 问题分析伪代码C代码示例解释在流网络问题中,我们通常会假设对于所有的节点v ∈ V,都存在一条从源点s到汇点t经过v的路径。然而,当这一假设不成立时,即存在某些节点u,使得不存在路径sut,我们需要证明在这种情况下,网络中…...
springboot的 nacos 配置获取不到导致启动失败及日志不输出问题
前言 问题 1. 本地启动应用时,一切正常,但是部署 docker 后,会因为获取不到 nacos 中的配置导致服务启动失败。 2.当 docker 中的服务一直重启,可能会突然某一次启动成功,之后只要不重新构建 docker 镜像&am…...
word poi-tl 图表功能增强,插入图表折线图、柱状图、饼状图
目录 问题解决问题poi-tl介绍 功能实现引入依赖功能介绍 功能实例饼图模版代码效果图 雷达图(模版同饼图)代码效果图 柱状图(模版同饼图)代码效果图 附加CustomCharts 工具类CustomChartSingleSeriesRenderData 数据对象CustomCha…...
SpringBoot 项目如何集成 JWT
SpringBoot 项目如何集成 JWT JWT JSON Web Token (JWT) 是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为 JSON 对象在各方之间安全地传输信息。 在 Oauth2 中,其实就是返回访问令牌 (access_token&#…...
如何查看电脑刷新率
Windows 系统 通过显示设置查看: 右键点击桌面空白处,选择 “显示设置”。在打开的窗口中,找到 “高级显示设置”。点击 “显示适配器属性”。在弹出的窗口中,选择 “监视器” 选项卡,即可看到当前的屏幕刷新率。使用 …...
MVC基础——市场管理系统(一)
文章目录 项目地址一、创建项目结构1.1 创建程序以及Controller1.2 创建View1.3 创建Models层,并且在Edit页面显示1.4 创建Layou模板页面1.5 创建静态文件css中间件二、Categories的CRUD2.1 使用静态仓库存储数据2.2 将Categorie的列表显示在页面中(List)2.3 创建_ViewImport.…...
面向对象中多态的含义
多态性的定义 多态(Polymorphism)是面向对象编程中的一个重要概念。它是指同一个函数名或操作符在不同的对象或情境下具有不同的行为。简单来说,就是 “多种形态”。例如,在一个图形处理程序中,有 “计算面积” 这个操…...
L2G3000-LMDeploy 量化部署实践
文章目录 LMDeploy 量化部署实践闯关任务环境配置W4A16 量化 KV cacheKV cache 量化Function call LMDeploy 量化部署实践闯关任务 环境配置 conda create -n lmdeploy python3.10 -y conda activate lmdeploy conda install pytorch2.1.2 torchvision0.16.2 torchaudio2.1.…...
ubuntu下的chattts 学习5:Example: self introduction
代码 import ChatTTS import torch import torchaudiochat ChatTTS.Chat() chat.load(compileFalse) # Set to True for better performance ################################### inputs_en """ chat T T S is a text to speech model designed for dialogu…...
IDEA的service窗口中启动类是灰色且容易消失
大家在学习Spring Cloud的过程中,随着项目的深入,会分出很多个微服务,当我们的服务数量大于等于三个的时候,IDEA会给我们的服务整理起来,类似于这样 但是当我们的微服务数量达到5个以上的时候,再启动服务的时候,服务的启动类就会变成灰色,而且还容易丢失 解决方法 我们按住…...
vue中pdf.js的使用,包括pdf显示,跳转指定页面,高亮关键词
目录 一、下载pdf.js 二、引入到本地的项目中 三、实现预览pdf 四、跳转到指定页面 五、利用pdf里面的find查找关键词 六、修改页面大小为实际大小 一、下载pdf.js https://github.com/mozilla/pdf.js 里面有很多的版本, 高版本的可能浏览器不兼容或者还要考…...
Cherno C++学习笔记 P32 字符串
这篇文章我们来讲字符串。字符串可以说是最重要的变量类型了,因为对字符串的读写极大地影响到我们的程序和用户之间的交互。甚至很多很庞大的程序就只是在处理字符串。 对于字符串,我们同时需要有关于数组和指针的关系,字符串的实现与数组是…...
【C++初阶】第7课—标准模版库STL(string_1)
文章目录 1. 什么是STL2. STL六大组件3. 标准库中string类3.1 auto关键字3.2 范围for3.3 string类的类型3.4 string类的常用接口(string类对象的常见构造)3.5 string的析构和赋值运算符重载3.6 string类对象的容量操作 1. 什么是STL STL(standard template library—标准模板库…...
GESP202306 一级【时间规划】题解(AC)
》》》点我查看「视频」详解》》》 AC_Code #include <bits/stdc.h> using namespace std;int main() {int h1, m1, h2, m2;cin >> h1 >> m1;cin >> h2 >> m2;h1 h1 * 60 m1;h2 h2 * 60 m2;cout << h2 - h1;return 0; }》》》点我查…...
React基础知识四 Hooks
什么是hooks? (coderwhy) hooks是react 16.8(2019年)出的新特性。 react有两种形式来创建组件——类式和函数式。在hooks之前类式组件就是react最主流的编程方式。 这个时候,函数式组件是非常鸡肋的,几乎没什么用。因…...
Linux服务器通用安全加固指南
1、保护引导过程(以Grub引导为例) 在 /etc/inittab 中添加 sp:S:respawn:/sbin/sulogin,以确保当切换到单用户模式时 运行级的配置要求输入 root 密码: cp /etc/inittab / etc/initab .bak vim /etc/inittab 退出:es…...
vsphere vcenter web 界面的介绍
这是主页的页面...
分类算法中的样本不平衡问题及其解决方案
一、样本不平衡问题概述 在机器学习的分类任务中,样本不平衡是指不同类别训练样本数量存在显著差异的现象。这一差异会给模型训练和性能评估带来挑战,尤其在处理少数类样本时,模型可能难以有效学习其特征。 以二分类为例,理想情况…...
[LitCTF 2023]破损的图片(初级)
[LitCTF 2023]破损的图片(初级) 我们下载附件得到一个没有后缀的文件,拖去010看一看,发现本来应该是文件头的那部分不大对劲,结合后面四个点以及IHDR,大致也应该知道是啥了 修改第一行为png 89 50 4E 47 0D 0A 1A 0A 00 00 00 …...
最新道客巴巴怎么免费下载文档方法
一、利用浏览器插件 插件自动识别下载:对于经常需要下载道客巴巴文档的人来说,安装浏览器插件是不错的选择。如Chrome浏览器上的一些插件(如PDF在线转换器等),安装后,在浏览器中打开道客巴巴文档ÿ…...
Bert的Transformer原理
多义词如何应对: 答:通过Self attention,不同的上下文,对同一个"苹果",得到截然不同的embedding激活值; Multi-head的作用: 有些类似CNN里用的多个卷积核得到多个Channel的特征图&…...
多人聊天室 NIO模型实现
NIO编程模型 Selector监听客户端不同的zhuangtai不同客户端触发不同的状态后,交由相应的handles处理Selector和对应的处理handles都是在同一线程上实现的 I/O多路复用 在Java中,I/O多路复用是一种技术,它允许单个线程处理多个输入/输出&…...
vue.js学习(day 20)
综合案例:购物车 数据渲染 构建cart购物车模块 准备后端接口服务环境 请求数据存入vuex cart.js // 新建购物车模块 import axios from axios export default {namespaced: true,state () {return {// 购物车数据 [{},{}]list: []}},mutations: {updateList (…...
蓝桥杯二分题
P1083 [NOIP2012 提高组] 借教室 题目描述 在大学期间,经常需要租借教室。大到院系举办活动,小到学习小组自习讨论,都需要向学校申请借教室。教室的大小功能不同,借教室人的身份不同,借教室的手续也不一样。 面对海量租…...
通过 CC-Link IEFB 转 Modbus RTU 网关达成三菱 FX5U PLC 与 RS485 温湿度传感器通信的配置实例展示
一. 案例背景 在某一化工厂现场,现场的设备是三菱FX5UPLC为了避免因为工作环境存在潮湿度较高性、较高温度等对员工造成不健康或危险的现象,现决定在现场装数多台温湿度传感器。通过捷米特JM-CCLKIE-RTU网关将三菱PLC及温湿度传感器连接起来并连接上位机…...
视频质量评价算法 DOVER 使用教程
DOVER 介绍 DOVER是为ICCV2023会议论文“Exploring Video Quality Assessment on User Generated Contents from Aesthetic and Technical Perspectives”提供的官方代码、演示和权重的项目。DOVER(Disentangled Objective Video Quality Evaluator)是一个用于评估用户生成内…...
视频编辑技术:一键生成混剪视频的AI技术应用
随着视频内容的爆炸式增长,视频编辑技术也在不断进步。本文将探讨如何利用AI技术,实现一键生成混剪视频,并自动添加配音和字幕,以提高视频编辑的效率和质量。 AI技术在视频编辑中的应用 AI技术在视频编辑领域的应用越来越广泛&am…...
精确的单向延迟测量:使用普通硬件和软件
论文标题:Precise One-way Delay Measurement with Common Hardware and Software(精确的单向延迟测量:使用普通硬件和软件) 作者信息:Maciej Muehleisen 和 Mazen Abdel Latif,来自Ericsson Research Eri…...
【博士每天一篇文献-COIL算法】Co-transport for class-incremental learning
1 介绍 年份:2021 会议:2021ACM Zhou D W, Ye H J, Zhan D C. Co-transport for class-incremental learning[C]//Proceedings of the 29th ACM International Conference on Multimedia. 2021: 1645-1654. 本文提出的算法是CO-transport for clas…...
AI与BI的火花:大语言模型如何重塑商业智能的未来
大家好,我是独孤风。 在当今这个数据驱动的时代,企业对于信息的需求如同对于氧气的需求一般至关重要。商业智能(BI)作为企业获取、分析和呈现数据的关键工具,正在经历一场深刻的变革,而这一变革的催化剂正是…...
C++设计模式(建造者、中介者、备忘录)
一、建造者模式 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。 示例: //房子(产品类) class House { private:int rooms;int windows;string decoration; public:void setRooms(int r) {rooms …...
【数据结构】遍历二叉树
遍历二叉树的算法描述(递归定义) 先序遍历 若二叉树为空,则空操作; 否则 (1)访问根节点 (2)先序遍历左子树 (3)先序遍历右子树 中序遍历 若二叉树为空…...
鸿蒙获取 APP 信息及手机信息
前言:获取 APP 版本信息可以通过 bundleManager.getBundleInfoForSelfSync(bundleFlags) 去获取,获取手机信息可以通过 kit.BasicServicesKit 库去获取,以下是封装好的工具类。 import bundleManager from ohos.bundle.bundleManager; impo…...
OpenWRT下深入了解IPv6——SLAAC
一、SLAAC(无状态地址自动配置) 1.基本原理 SLAAC 是 IPv6 中的一种地址自动配置机制,它允许设备根据网络中的路由器通告信息和自身的 MAC 地址自动生成 IPv6 地址。在 IPv6 网络中,MAC 地址长度为 48 位,而 IPv6 地…...
UE5水文章 UI按钮样式快捷复制黏贴
shift右键拷贝 shift右键黏贴...
迭代器模式的理解和实践
引言 在软件开发中,我们经常需要遍历容器对象(如数组、列表、集合等)中的元素。如果每个容器对象都实现自己的遍历算法,那么代码将会变得冗余且难以维护。为了解决这个问题,迭代器模式应运而生。迭代器模式是一种行为型…...
Python __func 与 _func 的区别引起的思考
文章目录 __function_function深入名称修饰机制名称修饰的目的实现原理 属性访问控制的高级模式基本模式扩展复杂的转换和验证逻辑带有日志和审计的访问控制 如果突然让我说一说 Python中的__function和_function有哪些不同的约定和用途,我好像一下子没法说出很多东…...
python学opencv|读取视频(二)制作gif
【1】引言 前述已经完成了图像和视频的读取学习,本次课学习制作gif格式动图。 【2】教程 实际上想制作gif格式动图是一个顺理成章的操作,完成了图像和视频的处理,那就自然而然会对gif的处理也产生兴趣。 不过在opencv官网、matplotlib官网…...
Redmi AX3000 (RA81) 路由器恢复原厂固件
最近给Redmi AX3000 (RA81) 刷了OpenWrt固件,但是存在各种小问题,因此决定刷回原厂固件。刷机之前保证能够访问路由器ssh,否则请百度救砖教程。 准备工具 Redmi AX3000 (RA81) 原厂分区文件 [github下载地址 / csdn下载地址]小米路由器修复…...
【调试工具】USB 转 UART 适配器(USB 转 TTL)
「USB 转 TTL 转换器」是错误的叫法,正确的叫法应该为 「USB 转 UART 适配器」。 Device connection 注意端口的交叉连接,Device1_TX<---->Device2_RX USB-to-UART adapter GND 记得接地。 使用: 当 TX,RX 需要电平为 0-3.3V 时&am…...
【YOLO部署Android安卓手机APP】YOLOv11部署到安卓实时目标检测识别——以火焰烟雾目标检测识别举例(可自定义更换其他目标)
前言:本项目基于YOLOv11部署到手机APP实现对火焰烟雾的检测识别,当然,以此你可以按照本项目开发步骤扩展更换为其他目标进行检测,例如更换为车牌、手势、人脸面部活动、人脸表情、火焰烟雾、行人、口罩、行为、水果、植物、农作物等等部署手机APP进行检测。本文为详细设计/…...
Python 中的 __slots__ 属性有什么作用?
__slots__ 是Python类中的一种特殊属性,它允许你显式地声明一个类的实例可以拥有的属性。 这不仅有助于节省内存,还能提高属性访问的速度,并且防止动态添加不属于设计的属性。 在大型项目或者对性能敏感的应用程序中,正确使用 _…...
【H2O2|全栈】Node.js与MySQL连接
目录 前言 开篇语 准备工作 初始配置 创建连接池 操作数据库 封装方法 结束语 前言 开篇语 本节讲解如何使用Node.js实现与MySQL数据库的连接,并将该过程进行函数封装。 与基础部分的语法相比,ES6的语法进行了一些更加严谨的约束和优化&#…...
【大数据技术基础】 课程 第3章 Hadoop的安装和使用 大数据基础编程、实验和案例教程(第2版)
第3章 Hadoop的安装和使用 3.1 Hadoop简介 Hadoop是Apache软件基金会旗下的一个开源分布式计算平台,为用户提供了系统底层细节透明的分布式基础架构。Hadoop是基于Java语言开发的,具有很好的跨平台特性,并且可以部署在廉价的计算机集群中。H…...
DDR4与DDR3服务器内存的关键区别有哪些?
内存作为服务器性能的关键组件之一,已经经历了从DDR3到DDR4的过渡。DDR4内存相较于DDR3在多个方面有所提升,包括速度、带宽、功耗以及数据传输效率等。然而,尽管DDR4内存在性能上占有优势,DDR3内存依然在一些特定场景中得到了广泛…...
OceanBase 的探索与实践
作者:来自 vivo 互联网数据库团队- Xu Shaohui 本文总结了目前我们遇到的痛点问题并通过 OceanBase 的技术方案解决了这些痛点问题,完整的描述了 OceanBase 的实施落地,通过迁移到 OceanBase 实践案例中遇到的问题与解决方案让大家能更好的了…...
2024年安全员-A证证模拟考试题库及安全员-A证理论考试试题
2024年安全员-A证模拟考试题库及理论考试试题(一) 单选题 根据《建筑施工企业主要负责人、项目负责人和专职安全生产管理人员安全生产管理规定》,项目负责人每月带班生产时间不得少于本月施工时间的( )。 A. 60% B. …...