Spring 事务原理总结六

news2025/3/13 6:52:13

不知不觉,关于Spring事务的文章已经写了五篇了。老实讲我自己不断质疑过自己:现在写这些文章还有意义吗?当前的市场已经成什么样了,为什么还要固守这落后的技术?但是贝索斯一次接受访谈的回答,让我写下去的决心更加坚定了,他是这样说的:相较于千变万化的事物,我更关注那些恒久不变的东西!

书归正传,上篇文章我们解决了《Spring事务原理总结四》这篇文章中提到的几个问题中的三个,其中“Spring事务异常回滚执行流程”这个问题,我们并没有梳理。今天就借这篇文章详细梳理一下。如果各位觉得这些文章对您有用,还请多多关注,谢谢!如果大家觉得有哪些地方梳理的不正确,也请大家多多指教,非常感谢!本篇文章梳理的比较啰嗦,大家可以跳过中间过程,看最后的总结。如果有些地方大家觉得不对,欢迎指出。

执行流程梳理

继续采用《Spring事务原理总结一》中的案例,修改TransferServiceImpl类中的check(String, String, BigDecimal)方法,具体代码如下所示:

@Override
public void check(String from, String to, BigDecimal money) {

    System.out.println("校验开始");
    System.out.println("校验中||...........");
    try {
        System.out.println(1 / 0);
        Thread.sleep(1000 * 5);
    } catch (InterruptedException e) {
    }
    System.out.println("校验中==...........");
    System.out.println("校验结束");

}

启动程序(用debug模式运行SpringTransactionApplication类即可),接着会在TransactionAspectSupport类的invokeWithinTransaction(Method method, @Nullable Class<?> targetClass, final InvocationCallback invocation)方法中的断点处停下,具体如下图所示:

启动程序(用debug模式运行SpringTransactionApplication类即可),接着会在TransactionAspectSupport类的invokeWithinTransaction(Method method, @Nullable Class<?> targetClass, final InvocationCallback invocation)方法中的断点处停下,具体如下图所示:

接着让我们继续执行代码,直到程序执行到下图所示的断点处停下,详情情况请参见下面这幅图:

这里需要注意一下,控制台并未输出TransferServiceImpl#check(String, String, BigDecimal)方法中要打印的任何内容,然后继续执行,结果如下图所示:

由图中可以看出,控制台输出了TransferServiceImpl#check(String, String, BigDecimal)方法要打印的内容,并且程序直接进入了catch逻辑。仔细观察会发现这个异常类型为java.lang.ArithmeticException: / by zero,这就是我们在代码中添加的System.out.println(1 / 0)抛出的,如果这个方法实际操作的是数据库,那catch逻辑中要执行的就是回滚操作了。先来看一下这个方法(这个方法——completeTransactionAfterThrowing(TransactionInfo, Throwable)——位于TransactionAspectSupport类中)的源码,如下所示:

protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
    if (txInfo != null && txInfo.getTransactionStatus() != null) {
       if (logger.isTraceEnabled()) {
          logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() +
                "] after exception: " + ex);
       }
       if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
          try {
             txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
          }
          catch (TransactionSystemException ex2) {
             logger.error("Application exception overridden by rollback exception", ex);
             ex2.initApplicationException(ex);
             throw ex2;
          }
          catch (RuntimeException | Error ex2) {
             logger.error("Application exception overridden by rollback exception", ex);
             throw ex2;
          }
       }
       else {
          // We don't roll back on this exception.
          // Will still roll back if TransactionStatus.isRollbackOnly() is true.
          try {
             txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
          }
          catch (TransactionSystemException ex2) {
             logger.error("Application exception overridden by commit exception", ex);
             ex2.initApplicationException(ex);
             throw ex2;
          }
          catch (RuntimeException | Error ex2) {
             logger.error("Application exception overridden by commit exception", ex);
             throw ex2;
          }
       }
    }
}

先来看一下下面这幅运行时图片,程序运行到if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex))处停下,具体见下图:

先让我们一起看一下txInfo.transactionAttribute.rollbackOn(ex)这句,这段代码的详细执行流程为:

  1. 执行DelegatingTransactionAttribute#rollbackOn(Throwable ex),注意DelegatingTransaction-Attribute中持有一个TransactionAttribute对象
  2. 调用RuleBasedTransactionAttribute#rollbackOn(Throwable ex)
  3. 调用DefaultTransactionAttribute#rollbackOn(Throwable ex)

对于这个调用过程,我们需要注意以下几点:

  • DelegatingTransactionAttribute类中持有的TransactionAttribute对象的实际类型是RuleBaseTransactionAttribute,这个类的rollbackOn(Throwable)方法实际上是一个代理方法,其会把处理转发给RuleBaseTransactionAttribute这个实际类型中的rollbackOn(Throwable)方法。所以DelegatingTransactionAttribute类中的rollbackOn(Throwable)方法的源码非常简单,具体如下所示:
public boolean rollbackOn(Throwable ex) {
    return this.targetAttribute.rollbackOn(ex);
}
  • RuleBasedTransactionAttribute这个类的rollbackOn(Throwable)方法是实际进行判断的地方,其中有一个rollbackRules对象,该对象的类型是List,其中存储的是一个一个的RollbackRuleAttribute类型的对象,首先来看一下rollbackOn(Throwable)这个方法的源码吧:
public boolean rollbackOn(Throwable ex) {
    RollbackRuleAttribute winner = null;
    int deepest = Integer.MAX_VALUE;

    if (this.rollbackRules != null) {
       for (RollbackRuleAttribute rule : this.rollbackRules) {
          int depth = rule.getDepth(ex);
          if (depth >= 0 && depth < deepest) {
             deepest = depth;
             winner = rule;
          }
       }
    }

    // User superclass behavior (rollback on unchecked) if no rule matches.
    if (winner == null) {
       return super.rollbackOn(ex);
    }

    return !(winner instanceof NoRollbackRuleAttribute);
}

通过这段源码不难发现,程序首先会遍历本类持有的rollbackRules对象,从中找到适合的数据并赋值给winner;接着判断winner对象是否为空,如果winner对象为空,则直接调用父类的rollbackOn(Throwable)方法,判断当前的异常类型是否合法,否则判断当前的winner对象是否为NoRollbackRuleAttribute类型,并取反,然后将结果返回给上级调用者。下面让我们看一下RollbackRuleAttribute类的继承结构,具体如下图所示:

接下来让我们看一下RuleBasedTransactionAttribute的父类DefaultTransactionAttribute类中的rollbackOn(Throwable)方法的源码

@Override
public boolean rollbackOn(Throwable ex) {
    return (ex instanceof RuntimeException || ex instanceof Error);
}

这个方法的主要作用就是判断当前的异常类型是否为RuntimeException或Error,如果是这两个就返回true,否则就返回false

看到这里我不禁有个问题:RuleBasedTransactionAttribute对象中的rollbackRules对象中的值是从哪里来的?接着让我们改造以下TransferServiceImpl类上的@Transactional注解,在其中增加一个rollbackFor属性,具体如下图所示:

然后启动程序,查看结果(注意断点在RuleBasedTransactionAttribute#rollbackOn()方法中),具体如下图所示:

从图中不难发现此时rollbackRules对象中的值确实是我们通过rollbackFor属性注入的两个异常类。但我又有点好奇RuleBasedTransactionAttribute#rollbackOn(Throwable)方法中的最后一句“!(winner instanceof NoRollbackRuleAttribute)”中的NoRollbackRuleAttribute这个是从哪里来的?还记得@Transactional注解上的noRollbackFor属性吗?让我们继续改造TransferServiceImpl类上的@Transactional注解,在其上添加noRollbackFor属性,具体如下图所示:

然后重新启动程序,查看结果(注意断点在RuleBasedTransactionAttribute#rollbackOn()方法中),具体如下图所示:

从图中可以看出,我们在注解中指定的CustomException被包装成了NoRollbackRuleAttribute类型的对象,由于前面我们更改了TransferServiceImpl类的check()方法,其最终会抛出一个名为CustomException类型的异常,所以这段代码返回的结果是false,最终也就不会执行TransactionAspectSupport中的completeTransactionAfterThrowing(TransactionInfo, Throwable)方法。注意TransferServiceImpl类中的check()方法的源码为:

public void check(String from, String to, BigDecimal money) throws Exception {

    System.out.println("校验开始");
    System.out.println("校验中||...........");
    try {
        // System.out.println(1 / 0);
        Thread.sleep(1000 * 5);
        throw new CustomException();
    } catch (InterruptedException e) {
    }
    System.out.println("校验中==...........");
    System.out.println("校验结束");

抛开这些问题,继续回到DefaultTransactionAttribute#rollbackFor()方法中,最终程序抛出的ArithmeticException异常经过该方法后返回的结果为true。具体如下图所示:

总体来看在不指定@Transactional注解的rollbackFor属性的时候,调用RuleBasedTransact-ionAttribute#rollbackOn()方法的作用就是判断当前异常是否为运行时异常(即RuntimeException或者Error),如果是则触发后面的回滚逻辑,如果不是则不触发后面的回滚逻辑

接下来让我们继续回到TransactionAspectSupport#completeTransactionAfterThrowing(Tra-nsactionInfo, Throwable)方法中,经过前面的判断,最终代码走到了txInfo.getTransactionManag-er().rollback(txInfo.getTransactionStatus())这行,具体如下图所示:

首先来看一下TransactionManager类的rollback(TransactionStatus)方法的源码(实际代码位于AbstractPlatformTransactionManager类中):

public final void rollback(TransactionStatus status) throws TransactionException {
    if (status.isCompleted()) {
       throw new IllegalTransactionStateException(
             "Transaction is already completed - do not call commit or rollback more than once per transaction");
    }

    DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
    processRollback(defStatus, false);
}

接着再来看一下processRollback()方法的源码,其主要作用就是执行真正的回滚逻辑。下面一起看一下processRollback()方法的源码:

private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
    try {
       boolean unexpectedRollback = unexpected;
       boolean rollbackListenerInvoked = false;

       try {
          triggerBeforeCompletion(status);

          if (status.hasSavepoint()) {
             if (status.isDebug()) {
                logger.debug("Rolling back transaction to savepoint");
             }
             this.transactionExecutionListeners.forEach(listener -> listener.beforeRollback(status));
             rollbackListenerInvoked = true;
             status.rollbackToHeldSavepoint();
          }
          else if (status.isNewTransaction()) {
             if (status.isDebug()) {
                logger.debug("Initiating transaction rollback");
             }
             this.transactionExecutionListeners.forEach(listener -> listener.beforeRollback(status));
             rollbackListenerInvoked = true;
             doRollback(status);
          }
          else {
             // Participating in larger transaction
             if (status.hasTransaction()) {
                if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
                   if (status.isDebug()) {
                      logger.debug("Participating transaction failed - marking existing transaction as rollback-only");
                   }
                   doSetRollbackOnly(status);
                }
                else {
                   if (status.isDebug()) {
                      logger.debug("Participating transaction failed - letting transaction originator decide on rollback");
                   }
                }
             }
             else {
                logger.debug("Should roll back transaction but cannot - no transaction available");
             }
             // Unexpected rollback only matters here if we're asked to fail early
             if (!isFailEarlyOnGlobalRollbackOnly()) {
                unexpectedRollback = false;
             }
          }
       }
       catch (RuntimeException | Error ex) {
          triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
          if (rollbackListenerInvoked) {
             this.transactionExecutionListeners.forEach(listener -> listener.afterRollback(status, ex));
          }
          throw ex;
       }

       triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
       if (rollbackListenerInvoked) {
          this.transactionExecutionListeners.forEach(listener -> listener.afterRollback(status, null));
       }

       // Raise UnexpectedRollbackException if we had a global rollback-only marker
       if (unexpectedRollback) {
          throw new UnexpectedRollbackException(
                "Transaction rolled back because it has been marked as rollback-only");
       }
    }
    finally {
       cleanupAfterCompletion(status);
    }
}

通过源码不难发现该方法接收一个TransactionStatus对象作为参数,这个对象封装了当前事务的状态信息。当需要回滚事务时(例如遇到未捕获异常或者显式调用TransactionTemplate或PlatformTransactionManager的rollback()方法时),框架会调用此方法来完成以下任务

  1. 清理资源:根据事务的具体类型(如JDBC、Hibernate、JTA等)清理与事务相关的一些资源,这可能包括数据库连接的回滚操作或者其他事务性资源的相应清理工作
  2. 更新事务状态:将事务状态标记为已回滚,确保后续不会尝试提交这个事务
  3. 触发监听器或回调:如果有注册的事务同步监听器(TransactionSynchronizationAdapter),则会触发相应的 afterCompletion 回调方法,通知它们事务已经回滚

总之,processRollback方法实现了事务生命周期中的“回滚”阶段,确保事务能够按照预期进行回滚,从而维持事务的原子性和一致性。通过debug跟踪和阅读源码,我们发现本示例最终走到了elsle if分支,该逻辑片段最终会调用DatasourceTransactionManager类的doRollback(DefaultTransactionStatus)方法,先来看一下这个方法的源码吧,具体如下所示:

protected void doRollback(DefaultTransactionStatus status) {
    DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
    Connection con = txObject.getConnectionHolder().getConnection();
    if (status.isDebug()) {
       logger.debug("Rolling back JDBC transaction on Connection [" + con + "]");
    }
    try {
       con.rollback();
    }
    catch (SQLException ex) {
       throw translateException("JDBC rollback", ex);
    }
}

通过源码,我们可以很清晰的看到,其最终就是通过调用Connection对象上的rollback()方法来完成事务的回滚的。

总结

文章进行到这里,前面遗留的问题基本上就梳理完了。首先通过这篇文章我们可以很清晰的看到Spring的设计者利用动态代理(cglib动态代理)及各种设计模式(责任链、模板等设计模式,其中模板设计模式在AbstractPlatformTransactionManager类体现的最为明显。该类中的rollback(TransactionStatus)方法规定了调用流程,即调用本类的processRollback(DefaultTransactionStatus, boolean)方法,而该方法又继续调用了本类中的模板方法doRollback(DefaultTransactionStatus),实际上调用的是其实现类中的方法完成了事务逻辑的抽离,使开发者可以花费更多的精力在业务代码的编写上。下面就让我们一起看一下AbstractPlatformTransactionManager类中的这几个方法的源码:

public final void rollback(TransactionStatus status) throws TransactionException {
    if (status.isCompleted()) {
       throw new IllegalTransactionStateException(
             "Transaction is already completed - do not call commit or rollback more than once per transaction");
    }

    DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
    processRollback(defStatus, false);
}
// 
private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
    try {
       boolean unexpectedRollback = unexpected;
       boolean rollbackListenerInvoked = false;

       try {
          triggerBeforeCompletion(status);

          if (status.hasSavepoint()) {
             if (status.isDebug()) {
                logger.debug("Rolling back transaction to savepoint");
             }
             this.transactionExecutionListeners.forEach(listener -> listener.beforeRollback(status));
             rollbackListenerInvoked = true;
             status.rollbackToHeldSavepoint();
          }
          else if (status.isNewTransaction()) {
             if (status.isDebug()) {
                logger.debug("Initiating transaction rollback");
             }
             this.transactionExecutionListeners.forEach(listener -> listener.beforeRollback(status));
             rollbackListenerInvoked = true;
             doRollback(status);
          }
          else {
             // Participating in larger transaction
             if (status.hasTransaction()) {
                if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
                   if (status.isDebug()) {
                      logger.debug("Participating transaction failed - marking existing transaction as rollback-only");
                   }
                   doSetRollbackOnly(status);
                }
                else {
                   if (status.isDebug()) {
                      logger.debug("Participating transaction failed - letting transaction originator decide on rollback");
                   }
                }
             }
             else {
                logger.debug("Should roll back transaction but cannot - no transaction available");
             }
             // Unexpected rollback only matters here if we're asked to fail early
             if (!isFailEarlyOnGlobalRollbackOnly()) {
                unexpectedRollback = false;
             }
          }
       }
       catch (RuntimeException | Error ex) {
          triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
          if (rollbackListenerInvoked) {
             this.transactionExecutionListeners.forEach(listener -> listener.afterRollback(status, ex));
          }
          throw ex;
       }

       triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
       if (rollbackListenerInvoked) {
          this.transactionExecutionListeners.forEach(listener -> listener.afterRollback(status, null));
       }

       // Raise UnexpectedRollbackException if we had a global rollback-only marker
       if (unexpectedRollback) {
          throw new UnexpectedRollbackException(
                "Transaction rolled back because it has been marked as rollback-only");
       }
    }
    finally {
       cleanupAfterCompletion(status);
    }
}
// 
protected abstract void doRollback(DefaultTransactionStatus status) throws TransactionException;

下面再回顾一下AbstractPlatformTransactionManager的实现类DataSourceTransactionManager中的doRollback()方法的源码(如果向了解这两个类的继承关系,可以浏览《Spring 事务原理总结三》这篇文章)

protected void doRollback(DefaultTransactionStatus status) {
    DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
    Connection con = txObject.getConnectionHolder().getConnection();
    if (status.isDebug()) {
       logger.debug("Rolling back JDBC transaction on Connection [" + con + "]");
    }
    try {
       con.rollback();
    }
    catch (SQLException ex) {
       throw translateException("JDBC rollback", ex);
    }
}

接着通过本章强化了我们对@Transactional注解的认识,该注解上的rollbackFor属性可以指定哪些异常发生时需要执行回滚操作,该注解上的noRollbackFor属性则可以指定哪些异常发生时不需要执行回滚操作(Spring设计者的这个设计,为开发者在开发中按照业务异常分别处理异常,提供了很大的便利)。另外通过本章我们也知道了通过这两个属性指定的异常最终会被分别包装成RollbackRuleAttribute和No RollbackRuleAttribute类型的对象,程序执行时会通过RuleBasedTransactionAttribute中的rollbackOn(Throwable ex)方法进行区分并加以判断,具体判断代码如下所示(含DelegatingTransactionAttribute和RuleBasedTransactionAttribute的父类DefaultTransactionAttribute中的rollbackOn(Throwable)方法):

/ DelegatingTransactionAttribute类中的rollbackOn(Throwable)方法
public boolean rollbackOn(Throwable ex) {
    return this.targetAttribute.rollbackOn(ex);
}
// RuleBasedTransactionAttribute类中的rollbackOn(Throwable)方法
public boolean rollbackOn(Throwable ex) {
    RollbackRuleAttribute winner = null;
    int deepest = Integer.MAX_VALUE;

    if (this.rollbackRules != null) {
       for (RollbackRuleAttribute rule : this.rollbackRules) {
          int depth = rule.getDepth(ex);
          if (depth >= 0 && depth < deepest) {
             deepest = depth;
             winner = rule;
          }
       }
    }

    // User superclass behavior (rollback on unchecked) if no rule matches.
    if (winner == null) {
       return super.rollbackOn(ex);
    }

    return !(winner instanceof NoRollbackRuleAttribute);
}
// RuleBasedTransactionAttribute的父类DefaultTransactionAttribute中的rollbackOn(Throwable)方法
@Override
public boolean rollbackOn(Throwable ex) {
    return (ex instanceof RuntimeException || ex instanceof Error);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1454383.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

抖店退货率太高,2024年没办法继续做了?买家市场下只能这样做

我是王路飞。 之前接到一位粉丝朋友的私信&#xff0c;说是自己做抖店也有段时间了&#xff0c;但现在没之前好做了。 一方面是行业内卷&#xff0c;自己做的无货源模式&#xff0c;没什么优势&#xff0c;利润太低了&#xff1b; 另一方面就是现在抖店的退货问题太严重了&a…

.netcore免费开源大型多用户商城系统源码

随着电商的不断扩大与市场占有率&#xff0c;现在基本每个企业或商家都会搭建自己的线上商城&#xff0c;而其中的多用户商城系统&#xff0c;由于可以让不同行业的商家入驻&#xff0c;同时也可以开设多个店铺受到了众多企业或商家的青睐。最几年&#xff0c;随着.net core C#…

Typora+PicGO+腾讯云COS做图床

文章目录 Typora&#xff0b;PicGO&#xff0b;腾讯云COS做图床一、为什么使用图床二、Typora、PicGO和腾讯云COS介绍三、下载Typora和PicGOTyporaPicGO 四、配置Typora、PicGO和腾讯云COS腾讯云COS配置PicGO配置Typora配置 Typora&#xff0b;PicGO&#xff0b;腾讯云COS做图床…

MyBatis完成单表的CRUD

提示&#xff1a;如果没有基础的可以看我的博客 > MyBatis概述与MyBatis入门程序 MyBatis完成单表的CRUD 一、准备工作二、Insert&#xff08;Create&#xff09;1.使用 map 的方式插入数据&#xff08;1&#xff09;编写 SQL 语句&#xff08;2&#xff09;编写测试代码&am…

数据分析(一) 理解数据

1. 描述性统计&#xff08;summary&#xff09; 对于一个新数据集&#xff0c;首先通过观察来熟悉它&#xff0c;可以打印数据相关信息来大致观察数据的常规特点&#xff0c;比如数据规模&#xff08;行数列数&#xff09;、数据类型、类别数量&#xff08;变量数目、取值范围…

文生图提示词:天气条件

天气和气候 --天气条件 Weather Conditions 涵盖了从基本的天气类型到复杂的气象现象&#xff0c;为描述不同的天气和气候条件提供了丰富的词汇。 Sunny 晴朗 Cloudy 多云 Overcast 阴天 Partly Cloudy 局部多云 Clear 清晰 Foggy 雾 Misty 薄雾 Hazy 朦胧 Rainy 下雨 Showers …

【Leetcode刷题笔记】27. 移除元素

原题链接 Leetcode 27. 移除元素 题目 给你一个数组 nums 和一个值 val&#xff0c;你需要原地移除所有数值等于 val 的元素&#xff0c;并返回移除后数组的新长度。不要使用额外的数组空间&#xff0c;你必须仅使用 O(1) 额外空间并原地修改输入数组。元素的顺序可以改变。…

js---webAPI

01 声明变量 js组成&#xff1a; DOM:操作网页内容的,开发页面内容特效和实现用户交互 BOM: DOM树&#xff1a;将 HTML 文档以树状结构直观的表现出来&#xff0c;我们称之为文档树或 DOM 树 文档树直观的体现了标签与标签之间的关系 CSS获取元素的方法 document.querySele…

stm32h750中文数据手册以及中文参考手册

stm32h750中文数据手册以及中文参考手册 &#x1f527;腾讯交互翻译&#xff1a;https://transmart.qq.com/zh-CN/file&#x1f528;PDF拆分合并工具集在线&#xff1a;https://www.ilovepdf.com/zh-cn ✨就目前来讲&#xff0c;stm32h750VB芯片价格&#xff0c;算是历史新低&a…

Mybatis-Plus常用技巧

一、官网网站&#xff1a; https://baomidou.com/pages/24112f/#%E7%89%B9%E6%80%A7 技巧和常用方法在官网使用 二、离线安装MybatisX插件-free 1、在idea官网下载插件的zip压缩包&#xff0c;上链接&#xff08;https://plugins.jetbrains.com/&#xff09; 2、在搜索框搜…

模型训练 —— AI算法初识

一、背景 AI算法中模型训练的主要目的是为了让机器学习算法从给定的标注数据中学习规律、特征和模式&#xff0c;并通过调整模型内部参数&#xff0c;使模型能够对未见过的数据进行准确预测或决策。具体来说&#xff1a; 1. **拟合数据**&#xff1a;模型通过训练来识别输入数…

Android下SF合成流程重学习之onMessageInvalidate

Android下SF合成流程重学习之onMessageInvalidate 引言 虽然看了很多关于Android Graphics图形栈的文章和博客&#xff0c;但是都没有形成自己的知识点。每次学习了&#xff0c;仅仅是学习了而已&#xff0c;没有形成自己的知识体系&#xff0c;这次趁着有时间&#xff0c;这次…

外汇天眼:8个平台被打击,有的因诈骗被处罚!

上周&#xff0c;澳大利亚证券和投资委员会&#xff08;ASIC&#xff09;取消了总部位于悉尼的Brava Capital的澳大利亚金融服务许可证&#xff08;AFSL&#xff09;&#xff0c;意大利公司和交易委员会CONSOB则将6个非法投资平台网站列入黑名单。另外&#xff0c;Reiwa-Capita…

uni-app使用uView打开弹出层后输入框聚焦时placeholder错位问题

这里就不放效果了&#xff0c;大概意思就是在使用uView的popus时&#xff0c;在底部弹出后&#xff0c;如果弹窗中的输入框会造成一瞬间的placeholder文字错位&#xff0c;这个问题的主要是因为uView安全区适配导致 uView相关文档 https://www.uviewui.com/components/safeAr…

视频如何去除水印?这三个方法赶紧收藏

在数字化内容的海洋中&#xff0c;视频已成为我们日常生活中不可或缺的一部分。然而&#xff0c;很多时候&#xff0c;我们渴望观看的优质视频内容却被水印所困扰。因此我们就需要视频去水印工具来帮助我们解决这些困扰。 一、水印云 水印云的视频去水印功能采用了先进的 AI …

自己部署一个牛逼的开发备忘录系统

目录 效果 简介 安装 使用 效果 简介 为开发人员分享快速参考备忘清单【速查表】。这是英文版 Reference 的中文版本&#xff0c;目的是为了方便自己的技术栈查阅&#xff0c;如果您提供一个清单&#xff0c;我将抽空搬运&#xff0c;立即撸起来 :)。如果您发现此处的备忘单…

数字化转型导师坚鹏:BLM政府数字化转型战略

BLM政府数字化转型战略 ——以BLM模型为核心&#xff0c;实现知行果合一 课程背景&#xff1a; 很多政府存在以下问题&#xff1a; 不知道如何系统地制定政府数字化转型战略&#xff1f; 不清楚其它政府数字化转型战略是如何制定的&#xff1f; 不知道其它政府数字化转…

分享一下,程序员为什么不喜欢关电脑?(个人观点仅供娱乐哈哈哈)

你是否曾经疑惑&#xff0c;为何身边的程序员朋友总是让电脑保持开机状态&#xff0c;仿佛与它们有着不解之缘&#xff1f;别急着给他们贴上“电脑迷”的标签&#xff0c;背后其实隐藏着许多合理的原因。今天&#xff0c;就让我们一同走进程序员的世界&#xff0c;探究他们为何…

Windows Server系列的时钟同步

文章目录 前言一、设置时钟同步二、调整同步周期总结前言 在Windows Server系列操作系统上部署网站应用有时候需要考虑服务器之间的时钟同步问题。当存在不同服务器之间的通讯访问并且要求一定的安全限定时,时钟同步的需求尤其显得比较迫切。本文将以Windows2012server为例,…

WorkPlus构建安全高效的内网通信平台,助力企业内部协作

为何选择WorkPlus作为内网通信软件的首选&#xff1f;首先&#xff0c;WorkPlus提供全面的内网通信解决方案。不论是文字聊天、语音通话还是实时视频会议&#xff0c;WorkPlus都能提供稳定、高效的通信体验&#xff0c;确保团队成员能够及时分享信息、快速沟通和协作。通过Work…