Seata学习 @GlobalTransactional注解的作用

news2024/11/27 1:37:39

Seata学习 @GlobalTransactional注解的作用

1.自动配置类 SeataAutoConfiguration

引入 seata与SpringBoot的整合包后,基于SpringBoot的自动配置,会往Spring容器中自动添加 SeataAutoConfiguration

image-20230615202634560

而 SeataAutoConfiguration 配置类又会往容器中添加bean GlobalTransactionScanner

image-20230615202840730

2.客户端核心类 GlobalTransactionScanner

继承关系图如下

image-20230615203008000

间接实现了 BeanPostProcessor 接口 ,在父类 AbstractAutoProxyCreator 中,重写了 BeanPostProcessor 接口的 postProcessAfterInitialization 方法,将在 GlobalTransactionScanner 初始化后调用此方法。

    public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
        if (bean != null) {
            Object cacheKey = this.getCacheKey(bean.getClass(), beanName);
            if (this.earlyProxyReferences.remove(cacheKey) != bean) {
                return this.wrapIfNecessary(bean, beanName, cacheKey);
            }
        }

        return bean;
    }

又调用 this.wrapIfNecessary()方法,由于 GlobalTransactionScanner 重写了这个方法,因此,会调用 GlobalTransactionScanner.wrapIfNecessary()

    protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        // do checkers
        if (!doCheckers(bean, beanName)) {
            return bean;
        }

        try {
            synchronized (PROXYED_SET) {
                if (PROXYED_SET.contains(beanName)) {
                    return bean;
                }
                interceptor = null;
                //check TCC proxy
                if (TCCBeanParserUtils.isTccAutoProxy(bean, beanName, applicationContext)) {
                    // init tcc fence clean task if enable useTccFence
                    TCCBeanParserUtils.initTccFenceCleanTask(TCCBeanParserUtils.getRemotingDesc(beanName), applicationContext);
                    //TCC interceptor, proxy bean of sofa:reference/dubbo:reference, and LocalTCC
                    interceptor = new TccActionInterceptor(TCCBeanParserUtils.getRemotingDesc(beanName));
                    ConfigurationCache.addConfigListener(ConfigurationKeys.DISABLE_GLOBAL_TRANSACTION,
                            (ConfigurationChangeListener)interceptor);
                } else {
                    Class<?> serviceInterface = SpringProxyUtils.findTargetClass(bean);
                    Class<?>[] interfacesIfJdk = SpringProxyUtils.findInterfaces(bean);

                    //是否存在 GlobalTransactional 注解
                    if (!existsAnnotation(new Class[]{serviceInterface})
                        && !existsAnnotation(interfacesIfJdk)) {
                        return bean;
                    }

                    if (globalTransactionalInterceptor == null) {
                        //创建连接器
                        globalTransactionalInterceptor = new GlobalTransactionalInterceptor(failureHandlerHook);
                        ConfigurationCache.addConfigListener(
                                ConfigurationKeys.DISABLE_GLOBAL_TRANSACTION,
                                (ConfigurationChangeListener)globalTransactionalInterceptor);
                    }
                    interceptor = globalTransactionalInterceptor;
                }

                LOGGER.info("Bean[{}] with name [{}] would use interceptor [{}]", bean.getClass().getName(), beanName, interceptor.getClass().getName());
                //当前bean是否已经被代理
                if (!AopUtils.isAopProxy(bean)) {
                    //创建代理类
                    bean = super.wrapIfNecessary(bean, beanName, cacheKey);
                } else {
                    AdvisedSupport advised = SpringProxyUtils.getAdvisedSupport(bean);
                    //获取切面
                    Advisor[] advisor = buildAdvisors(beanName, getAdvicesAndAdvisorsForBean(null, null, null));
                    int pos;
                    for (Advisor avr : advisor) {
                        // Find the position based on the advisor's order, and add to advisors by pos
                        pos = findAddSeataAdvisorPosition(advised, avr);
                        advised.addAdvisor(pos, avr);
                    }
                }
                PROXYED_SET.add(beanName);
                return bean;
            }
        } catch (Exception exx) {
            throw new RuntimeException(exx);
        }
    }

假设当前时默认AT模式:

获取当前bean的字节码对象

 Class<?> serviceInterface = SpringProxyUtils.findTargetClass(bean);

判断当前字节码对象上或者方法上是否有 GlobalTransactional 注解

if (!this.existsAnnotation(new Class[]{serviceInterface}) && !this.existsAnnotation(interfacesIfJdk)) {
                                return bean;
                            }
 private boolean existsAnnotation(Class<?>[] classes) {
        if (CollectionUtils.isNotEmpty(classes)) {
            for (Class<?> clazz : classes) {
                //当前类上是否有GlobalTransactional注解
                if (clazz == null) {
                    continue;
                }
                GlobalTransactional trxAnno = clazz.getAnnotation(GlobalTransactional.class);
                if (trxAnno != null) {
                    return true;
                }
                Method[] methods = clazz.getMethods();
                for (Method method : methods) {
                    //当前方法上是否有GlobalTransactional注解
                    trxAnno = method.getAnnotation(GlobalTransactional.class);
                    if (trxAnno != null) {
                        return true;
                    }

                    GlobalLock lockAnno = method.getAnnotation(GlobalLock.class);
                    if (lockAnno != null) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

创建代理类是针对整个类而言,就算这个类中只有一个方法上有 GlobalTransactional 注解,其他方法在被调用时也会走增强逻辑,

seata肯定会在后续方法调用中判断

创建全局事务拦截器 参数 failureHandlerHook 是一个事务处理失败回调钩子,默认为 DefaultFailureHandlerImpl,如有需要可以自定义,做事务失败时邮件提醒等等其他功能开发

 this.globalTransactionalInterceptor = new GlobalTransactionalInterceptor(this.failureHandlerHook);

当一个带有 @GlobalTransactional 注解的方法被调用时,GlobalTransactionalInterceptor 会拦截该方法并开启一个全局事务。在该方法执行完毕后,GlobalTransactionalInterceptor 会根据事务执行结果决定是提交还是回滚该事务。因此,GlobalTransactionalInterceptor 会在事务开始和结束时被调用。

调用父类的AbstractAutoProxyCreator.wrapIfNecessary 方法

 bean = super.wrapIfNecessary(bean, beanName, cacheKey);
rotected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
            return bean;
        } else if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
            return bean;
        } else if (!this.isInfrastructureClass(bean.getClass()) && !this.shouldSkip(bean.getClass(), beanName)) {
        		//将GlobalTransactionalInterceptor 取出
            Object[] specificInterceptors = this.getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, (TargetSource)null);
            if (specificInterceptors != DO_NOT_PROXY) {
                this.advisedBeans.put(cacheKey, Boolean.TRUE);
                Object proxy = this.createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
                this.proxyTypes.put(cacheKey, proxy.getClass());
                return proxy;
            } else {
                this.advisedBeans.put(cacheKey, Boolean.FALSE);
                return bean;
            }
        } else {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }
    }

创建代理类

 Object proxy = this.createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));

3.全局事务拦截器 GlobalTransactionalInterceptor

找到invoke方法

public Object invoke(final MethodInvocation methodInvocation) throws Throwable {
				//获取当前方法所属对象
        Class<?> targetClass = methodInvocation.getThis() != null ? AopUtils.getTargetClass(methodInvocation.getThis()) : null;
        //获取方法对象
        Method specificMethod = ClassUtils.getMostSpecificMethod(methodInvocation.getMethod(), targetClass);
        //不能时Object对象的方法
        if (specificMethod != null && !specificMethod.getDeclaringClass().equals(Object.class)) {
        		//寻找桥接方法
            Method method = BridgeMethodResolver.findBridgedMethod(specificMethod);
            //获取方法上的全局事务注解对象
            GlobalTransactional globalTransactionalAnnotation = (GlobalTransactional)this.getAnnotation(method, targetClass, GlobalTransactional.class);
            //获取方法上的全局锁注解对象
            GlobalLock globalLockAnnotation = (GlobalLock)this.getAnnotation(method, targetClass, GlobalLock.class);
            boolean localDisable = this.disable || ATOMIC_DEGRADE_CHECK.get() && degradeNum >= degradeCheckAllowTimes;
            if (!localDisable) {
            		//方法上面要有 globalTransactionalAnnotation 注解,否则直接调用原有方法
                if (globalTransactionalAnnotation != null || this.aspectTransactional != null) {
                    AspectTransactional transactional;
                    if (globalTransactionalAnnotation != null) {
                    		//创建一个全局事务切面配置类
                        transactional = new AspectTransactional(globalTransactionalAnnotation.timeoutMills(), globalTransactionalAnnotation.name(), globalTransactionalAnnotation.rollbackFor(), globalTransactionalAnnotation.rollbackForClassName(), globalTransactionalAnnotation.noRollbackFor(), globalTransactionalAnnotation.noRollbackForClassName(), globalTransactionalAnnotation.propagation(), globalTransactionalAnnotation.lockRetryInterval(), globalTransactionalAnnotation.lockRetryTimes(), globalTransactionalAnnotation.lockStrategyMode());
                    } else {
                        transactional = this.aspectTransactional;
                    }
										//增强方法  处理全局事务
                    return this.handleGlobalTransaction(methodInvocation, transactional);
                }

                if (globalLockAnnotation != null) {
                    return this.handleGlobalLock(methodInvocation, globalLockAnnotation);
                }
            }
        }

        return methodInvocation.proceed();
    }

4.真正处理全局事务的方法 handleGlobalTransaction

    Object handleGlobalTransaction(final MethodInvocation methodInvocation, final AspectTransactional aspectTransactional) throws Throwable {
        boolean succeed = true;

        Object var4;
        try {
        		// 事务管理模版类执行execute方法
            var4 = this.transactionalTemplate.execute(new TransactionalExecutor() {
                public Object execute() throws Throwable {
                    return methodInvocation.proceed();
                }

                public String name() {
                    String name = aspectTransactional.getName();
                    return !StringUtils.isNullOrEmpty(name) ? name : GlobalTransactionalInterceptor.this.formatMethod(methodInvocation.getMethod());
                }

                public TransactionInfo getTransactionInfo() {
                    int timeout = aspectTransactional.getTimeoutMills();
                    if (timeout <= 0 || timeout == 60000) {
                        timeout = GlobalTransactionalInterceptor.defaultGlobalTransactionTimeout;
                    }

                    TransactionInfo transactionInfo = new TransactionInfo();
                    transactionInfo.setTimeOut(timeout);
                    transactionInfo.setName(this.name());
                    transactionInfo.setPropagation(aspectTransactional.getPropagation());
                    transactionInfo.setLockRetryInterval(aspectTransactional.getLockRetryInterval());
                    transactionInfo.setLockRetryTimes(aspectTransactional.getLockRetryTimes());
                    transactionInfo.setLockStrategyMode(aspectTransactional.getLockStrategyMode());
                    Set<RollbackRule> rollbackRules = new LinkedHashSet();
                    Class[] var4 = aspectTransactional.getRollbackFor();
                    int var5 = var4.length;

                    int var6;
                    Class rbRule;
                    for(var6 = 0; var6 < var5; ++var6) {
                        rbRule = var4[var6];
                        rollbackRules.add(new RollbackRule(rbRule));
                    }

                    String[] var8 = aspectTransactional.getRollbackForClassName();
                    var5 = var8.length;

                    String rbRulex;
                    for(var6 = 0; var6 < var5; ++var6) {
                        rbRulex = var8[var6];
                        rollbackRules.add(new RollbackRule(rbRulex));
                    }

                    var4 = aspectTransactional.getNoRollbackFor();
                    var5 = var4.length;

                    for(var6 = 0; var6 < var5; ++var6) {
                        rbRule = var4[var6];
                        rollbackRules.add(new NoRollbackRule(rbRule));
                    }

                    var8 = aspectTransactional.getNoRollbackForClassName();
                    var5 = var8.length;

                    for(var6 = 0; var6 < var5; ++var6) {
                        rbRulex = var8[var6];
                        rollbackRules.add(new NoRollbackRule(rbRulex));
                    }

                    transactionInfo.setRollbackRules(rollbackRules);
                    return transactionInfo;
                }
            });
        } catch (TransactionalExecutor.ExecutionException var9) {
        		//异常回调方法
            TransactionalExecutor.Code code = var9.getCode();
            switch (code) {
                case RollbackDone:
                    throw var9.getOriginalException();
                case BeginFailure:
                    succeed = false;
                    this.failureHandler.onBeginFailure(var9.getTransaction(), var9.getCause());
                    throw var9.getCause();
                case CommitFailure:
                    succeed = false;
                    this.failureHandler.onCommitFailure(var9.getTransaction(), var9.getCause());
                    throw var9.getCause();
                case RollbackFailure:
                    this.failureHandler.onRollbackFailure(var9.getTransaction(), var9.getOriginalException());
                    throw var9.getOriginalException();
                case RollbackRetrying:
                    this.failureHandler.onRollbackRetrying(var9.getTransaction(), var9.getOriginalException());
                    throw var9.getOriginalException();
                case TimeoutRollback:
                    this.failureHandler.onTimeoutRollback(var9.getTransaction(), var9.getOriginalException());
                    throw var9.getCause();
                default:
                    throw new ShouldNeverHappenException(String.format("Unknown TransactionalExecutor.Code: %s", code));
            }
        } finally {
            if (ATOMIC_DEGRADE_CHECK.get()) {
                EVENT_BUS.post(new DegradeCheckEvent(succeed));
            }

        }

        return var4;
    }

5.事务管理模版类 TransactionalTemplate

    public Object execute(TransactionalExecutor business) throws Throwable {
        // 1. Get transactionInfo
        TransactionInfo txInfo = business.getTransactionInfo();
        if (txInfo == null) {
            throw new ShouldNeverHappenException("transactionInfo does not exist");
        }
        // 1.1 Get current transaction, if not null, the tx role is 'GlobalTransactionRole.Participant'.
        GlobalTransaction tx = GlobalTransactionContext.getCurrent();

        // 1.2 Handle the transaction propagation.
        Propagation propagation = txInfo.getPropagation();
        SuspendedResourcesHolder suspendedResourcesHolder = null;
        try {
            switch (propagation) {
                case NOT_SUPPORTED:
                    // If transaction is existing, suspend it.
                    if (existingTransaction(tx)) {
                        suspendedResourcesHolder = tx.suspend();
                    }
                    // Execute without transaction and return.
                    return business.execute();
                case REQUIRES_NEW:
                    // If transaction is existing, suspend it, and then begin new transaction.
                    if (existingTransaction(tx)) {
                        suspendedResourcesHolder = tx.suspend();
                        tx = GlobalTransactionContext.createNew();
                    }
                    // Continue and execute with new transaction
                    break;
                case SUPPORTS:
                    // If transaction is not existing, execute without transaction.
                    if (notExistingTransaction(tx)) {
                        return business.execute();
                    }
                    // Continue and execute with new transaction
                    break;
                case REQUIRED:
                    // If current transaction is existing, execute with current transaction,
                    // else continue and execute with new transaction.
                    break;
                case NEVER:
                    // If transaction is existing, throw exception.
                    if (existingTransaction(tx)) {
                        throw new TransactionException(
                            String.format("Existing transaction found for transaction marked with propagation 'never', xid = %s"
                                    , tx.getXid()));
                    } else {
                        // Execute without transaction and return.
                        return business.execute();
                    }
                case MANDATORY:
                    // If transaction is not existing, throw exception.
                    if (notExistingTransaction(tx)) {
                        throw new TransactionException("No existing transaction found for transaction marked with propagation 'mandatory'");
                    }
                    // Continue and execute with current transaction.
                    break;
                default:
                    throw new TransactionException("Not Supported Propagation:" + propagation);
            }

            // 1.3 If null, create new transaction with role 'GlobalTransactionRole.Launcher'.
            if (tx == null) {
                tx = GlobalTransactionContext.createNew();
            }

            // set current tx config to holder
            GlobalLockConfig previousConfig = replaceGlobalLockConfig(txInfo);

            try {
                // 2. If the tx role is 'GlobalTransactionRole.Launcher', send the request of beginTransaction to TC,
                //    else do nothing. Of course, the hooks will still be triggered.
                beginTransaction(txInfo, tx);

                Object rs;
                try {
                    // Do Your Business
                    rs = business.execute();
                } catch (Throwable ex) {
                    // 3. The needed business exception to rollback.
                    completeTransactionAfterThrowing(txInfo, tx, ex);
                    throw ex;
                }

                // 4. everything is fine, commit.
                commitTransaction(tx);

                return rs;
            } finally {
                //5. clear
                resumeGlobalLockConfig(previousConfig);
                triggerAfterCompletion();
                cleanUp();
            }
        } finally {
            // If the transaction is suspended, resume it.
            if (suspendedResourcesHolder != null) {
                tx.resume(suspendedResourcesHolder);
            }
        }
    }

主要一下几步:

  1. 获取全局事务信息类TransactionInfo
  2. 获取全局事务对象,如果当前全局事务上下文中已经有了全局事务,那么当前事务肯定是分支事务
  3. 处理事务传播行为
  4. 如果当前全局事务对象为null,创建一个全局事务
  5. 开启全局事务
  6. 执行目标方法
  7. 提交事务/出现异常回滚事务
  8. (一定会执行) 释放全局锁/回调方法
   try {
                // 2. If the tx role is 'GlobalTransactionRole.Launcher', send the request of beginTransaction to TC,
                //    else do nothing. Of course, the hooks will still be triggered.
                beginTransaction(txInfo, tx);

                Object rs;
                try {
                    // Do Your Business
                    rs = business.execute();
                } catch (Throwable ex) {
                    // 3. The needed business exception to rollback.
                    completeTransactionAfterThrowing(txInfo, tx, ex);
                    throw ex;
                }

                // 4. everything is fine, commit.
                commitTransaction(tx);

                return rs;
            } finally {
                //5. clear
                resumeGlobalLockConfig(previousConfig);
                triggerAfterCompletion();
                cleanUp();
            }
        } finally {
            // If the transaction is suspended, resume it.
            if (suspendedResourcesHolder != null) {
                tx.resume(suspendedResourcesHolder);
            }

6. 全局事务的两个角色

全局事务一共有两个角色

  1. 事务创建者 :创建新xid
  2. 事务参与者 : 绑定xid
public enum GlobalTransactionRole {

    /**
     * The Launcher.
     */
    // The one begins the current global transaction.
    Launcher,

    /**
     * The Participant.
     */
    // The one just joins into a existing global transaction.
    Participant
}

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

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

相关文章

基于Java汽车客运站管理系统设计实现(源码+lw+部署文档+讲解等)

博主介绍&#xff1a; ✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战 ✌ &#x1f345; 文末获取源码联系 &#x1f345; &#x1f447;&#x1f3fb; 精…

TrafficRoute:一体化的DNS解析和流量调度套件

TrafficRoute是火山引擎推出的解析调度套件&#xff0c;它实现了“一体化”的解析和调度服务&#xff0c;覆盖「公网解析」、「私网解析」、「流量调度/容灾」等场景&#xff0c;提供高性能、高可用、精准、及时、安全和可定制的解析调度产品集。 TrafficRoute具备云解析 DNS、…

华为OD机试真题 JavaScript 实现【字符串通配符】【2022Q4 200分】

一、题目描述 问题描述&#xff1a;在计算机中&#xff0c;通配符一种特殊语法&#xff0c;广泛应用于文件搜索、数据库、正则表达式等领域。现要求各位实现字符串通配符的算法。 要求&#xff1a; 实现如下2个通配符&#xff1a; &#xff1a;匹配0个或以上的字符&#xf…

【一文解决】已安装CUDA与Pytorch但torch.cuda.is_available()为False

目录 问题描述总览&#xff1a;导致问题的原因可能1&#xff1a;CUDA版本与驱动程序不兼容可能2&#xff1a;CUDA库的路径设置存在问题可能3&#xff1a;PyTorch版本与CUDA版本不匹配可能4&#xff1a;编译问题可能5&#xff1a;软件包或库冲突写在最后 问题描述 已经安装CUDA…

2023 陕西省大学生网络安全技能大赛 --- 高职组 Crypto ezmath

文章目录 题目解题过程 题目 from Crypto.Util.number import * from secret import y,a,b flagbflag{} l len(flag) m1, m2 flag[: l // 2], flag[l // 2:]x bytes_to_long(m1) c bytes_to_long(m2)assert (x**21)*(y**21)-2*(xy)*(x*y1)gift-4*x*y 4*b**6-2*a**33*a*c …

JavaScript案例分享:让前端开发者抓狂的按钮

前言 我分享一个前端案例&#xff0c;代码来源我不确定&#xff0c;可能是我某个编程技术交流群的群友分享的。由于我设置了自动下载文件&#xff0c;今天在查看微信文件时偶然发现了这个案例&#xff0c;查看下载日期是6月7日&#xff0c;所以无法确定到底是哪个群友分享的。但…

618,你会入手哪些书?【文末送书】

好书分享 前沿技术人工智能半导体新一代通信与信息技术网络空间安全参与规则 一年一度的618又到啦&#xff01;今年的618就不要乱买啦&#xff0c;衣服买多了会被淘汰&#xff0c;电子产品买多了会过时&#xff0c;零食买多了会增肥&#xff0c;最后怎么看都不划算。可是如果你…

超强整理,性能测试-常用服务器性能指标分析总结,一篇概全...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 压测过程中&#…

Hive SQL DML

Hive SQL DML 本节所需数据集 数据集 提取码&#xff1a;rkun ⛵加载数据 Load 加载&#xff0c;装载将数据文件移动到与Hive表对应位置&#xff0c;移动时是纯复制&#xff0c;移动操作。纯复制移动指数据load加载到表中&#xff0c;hive不会对表中数据内容进行任何变换&…

Cache技术在星辰处理器中的应用

Cache技术在星辰处理器中的应用-修复MicroPython在MM32F5上启动慢的问题 文章目录 Cache技术在星辰处理器中的应用-修复MicroPython在MM32F5上启动慢的问题引言Cache的工作原理需要关闭DCache的情况鱼和熊掌都想要使用内存保护单元MPU使用内存隔离/同步指令 总结参考文献 引言 …

catkin cmake官方教程解读以及资料补充

这里写目录标题 cmakei下载cmake 官方教程教程1step1最低版本 报错报错2 vscode 路径没有配置好setting.json通过该方式打开的似乎是一个全局的文件&#xff0c;可以为本工作文件夹下设置一个本地的吗 报错3配置cmake工具链准确的流程报错4 cpp中main函数返回值问题结果 官方教…

虚拟机centos7无法正常启动:Generating“/run/initramfs/rdsosreport.txt“

一、问题描述 1.出现问题的原因 Centos 7 断电导致 &#xff0c;最近电脑老是自己蓝屏&#xff0c;然后重启电脑&#xff0c;一个月里断断续续可能有个3次左右&#xff0c;突然发现启动就这个问题&#xff0c;估计是虚拟机异常物理断电导致的系统磁盘出错了 2.具体的报错信息…

打开冒险岛提示丢失vcruntime140.dll的解决方法

今天准备打开冒险岛软件的时候&#xff0c;当打开我自己的冒险岛软件后&#xff0c;弹出了一个对话框&#xff0c;内容是&#xff1a;由于找不到vcruntime140_1.dll&#xff0c;无法继续执行代码。重新安装程序可能会解决此问题。 我很纳闷&#xff0c;前几天还好好着呢。于是…

【ARIMA-WOA-LSTM】合差分自回归移动平均方法-鲸鱼优化-长短期记忆神经网络研究(Python代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

vue新手入门实践教程

介绍vue运行环境的搭建、vue项目的初步构建与运行、使用element-ui组件构建页面内容、使用页面模块与路由设置等。基于此教程&#xff0c;可以初步实现一个静态网页&#xff0c;并对vue项目由一定理解。 1、基本环境安装 vue项目的编译运行依赖nodejs环境&#xff0c;故需要下…

使用rasterio计算tif文件中两点之间的物理实际距离

假设有一张含有地理坐标信息的tif格式的图片及其对应的jpg或者png格式的普通图片 如下图所示&#xff1a; 其中第一张为tif格式的地理信息图&#xff0c;第二张为按照一定比例下采样之后转换得到的普通jpg图片 如何计算jpg图片中任意两点之间的实际距离呢&#xff1f; 比如&a…

终于等到你:期待已久的CAD .NET 15 Crack

期待已久的CAD .NET 15 现已推出&#xff01;新包包括一个.NET 6 框架构建。目前&#xff0c;它仅与 Windows 兼容&#xff0c;但我们计划在未来的版本中添加对 Linux 的支持。 我们还进行了一系列更改以增强库的稳定性并改进其导入和导出功能。他们来了&#xff1a; 改进了 DW…

Scala--03

第6章 面向对象 Scala 的面向对象思想和Java 的面向对象思想和概念是一致的。 Scala 中语法和 Java 不同&#xff0c;补充了更多的功能。 6.1类和对象详解 6.1.1组成结构 构造函数: 在创建对象的时候给属性赋值 成员变量: 成员方法(函数) 局部变量 代码块 6.1.2构造器…

详解c++---map的介绍

目录标题 map容器的介绍pair的介绍map的构造函数insert函数make_pair函数find函数map的[ ]重载multimap map容器的介绍 通过之前的学习想必大家对set容器的理解应该非常的深刻了&#xff0c;我们知道他的底层是一个k结构的搜索二叉树&#xff0c;可以对数据进行去重并排序&…

Redis实现分布式锁详解

Redis实现分布式锁详解 一 分布式锁简介二 Redis实现分布式锁核心思路三 Redis实现分布式锁实践3.1 锁的基本接口3.2 加锁解锁逻辑3.3 修改业务逻辑3.4 单元测试观察结果 四 Redis分布式锁误删情况4.1.Redis分布式锁误删情况逻辑说明&#xff1a;4.2 解决Redis分布式锁误删问题…