【SpringCloud-Seata客户端源码分析01】

news2024/10/25 8:19:01

文章目录

  • 启动seata客户端
    • 1.导入依赖
    • 2.自动装配
  • 发送请求的核心方法
  • 客户端开启事务的核心流程
  • 服务端分布式事务的处理机制

启动seata客户端

1.导入依赖

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.12.RELEASE</version>
    </parent>
    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <!-- 选择SpringCloudAlibaba版本的时候,一定要参考官网的建议,否则会有问题 -->
        <spring-cloud.version>Hoxton.SR12</spring-cloud.version>
        <spring-cloud-alibaba.version>2.2.9.RELEASE</spring-cloud-alibaba.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring-cloud-alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>

    </dependencyManagement>
    <dependencies>
        <!-- nacos服务注册与发现 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!--引入seata-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

客户端启动流程图
在这里插入图片描述

2.自动装配

自动装配的核心类
在这里插入图片描述
在SeataAutoConfiguration我们找到对应注入的类GlobalTransactionScanner,通过名称我们应该推算出,他应该是对应@GlobalTransaction进行扫描,然后注入到容器
我们先看下GlobalTransactionScanner类继承的类
在这里插入图片描述
创建的代理类

  protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        if (!this.doCheckers(bean, beanName)) {
            return bean;
        } else {
            try {
                synchronized(PROXYED_SET) {
                	//如果代理已经存在直接返回bean对象
                    if (PROXYED_SET.contains(beanName)) {
                        return bean;
                    } else {
                        this.interceptor = null;
                        //判断是否是TCC模式
                        if (TCCBeanParserUtils.isTccAutoProxy(bean, beanName, this.applicationContext)) {
                            TCCBeanParserUtils.initTccFenceCleanTask(TCCBeanParserUtils.getRemotingDesc(beanName), this.applicationContext);
                            this.interceptor = new TccActionInterceptor(TCCBeanParserUtils.getRemotingDesc(beanName));
                            ConfigurationCache.addConfigListener("service.disableGlobalTransaction", new ConfigurationChangeListener[]{(ConfigurationChangeListener)this.interceptor});
                        } else {
                            Class<?> serviceInterface = SpringProxyUtils.findTargetClass(bean);
                            Class<?>[] interfacesIfJdk = SpringProxyUtils.findInterfaces(bean);
                       	//判断是否是被注解@globalTransactional或者@GlobalLock代理的类

                            if (!this.existsAnnotation(new Class[]{serviceInterface}) && !this.existsAnnotation(interfacesIfJdk)) {
                                return bean;
                            }
                            //创建globalTransactionalInterceptor拦截器
                            if (this.globalTransactionalInterceptor == null) {
                                this.globalTransactionalInterceptor = new GlobalTransactionalInterceptor(this.failureHandlerHook);
                                ConfigurationCache.addConfigListener("service.disableGlobalTransaction", new ConfigurationChangeListener[]{(ConfigurationChangeListener)this.globalTransactionalInterceptor});
                            }

                            this.interceptor = this.globalTransactionalInterceptor;
                        }

                        LOGGER.info("Bean[{}] with name [{}] would use interceptor [{}]", new Object[]{bean.getClass().getName(), beanName, this.interceptor.getClass().getName()});
                        if (!AopUtils.isAopProxy(bean)) {
                            bean = super.wrapIfNecessary(bean, beanName, cacheKey);
                        } else {
                            AdvisedSupport advised = SpringProxyUtils.getAdvisedSupport(bean);
                            //获取拦截器
                            Advisor[] advisor = this.buildAdvisors(beanName, this.getAdvicesAndAdvisorsForBean((Class)null, (String)null, (TargetSource)null));
                            Advisor[] var8 = advisor;
                            int var9 = advisor.length;

                            for(int var10 = 0; var10 < var9; ++var10) {
                                Advisor avr = var8[var10];
                                int pos = this.findAddSeataAdvisorPosition(advised, avr);
                                advised.addAdvisor(pos, avr);
                            }
                        }

                        PROXYED_SET.add(beanName);
                        return bean;
                    }
                }
            } catch (Exception var14) {
                throw new RuntimeException(var14);
            }
        }
    }

事务管理器TM,资源管理器RM初始化实在实例化之后进行这个是在GlobalTransactionScanner
继承的InitializingBean的afterPropertiesSet方法中实现InitializingBean接口的使用
实现InitializingBean接口的bean,在Spring容器初始化并设置所有bean属性后,会调用其afterPropertiesSet()方法。这通常用于在bean的属性全部设置完毕后需要进行的一些自定义初始化工作,例如验证属性或建立资源连接。


    public void afterPropertiesSet() {
    //判断是否开启事务
        if (this.disableGlobalTransaction) {
            if (LOGGER.isInfoEnabled()) {
                LOGGER.info("Global transaction is disabled.");
            }
			//添加监听器  
          ConfigurationCache.addConfigListener("service.disableGlobalTransaction", new ConfigurationChangeListener[]{this});
        } else {
        //CAS将初始化置换成True
            if (this.initialized.compareAndSet(false, true)) {
            	//初始化客户端
                this.initClient();
            }

        }
    }

初始化客户端的代码

private void initClient() {
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("Initializing Global Transaction Clients ... ");
        }

        if ("my_test_tx_group".equals(this.txServiceGroup)) {
            LOGGER.warn("the default value of seata.tx-service-group: {} has already changed to {} since Seata 1.5, please change your default configuration as soon as possible and we don't recommend you to use default tx-service-group's value provided by seata", "my_test_tx_group", "default_tx_group");
        }

        if (!StringUtils.isNullOrEmpty(this.applicationId) && !StringUtils.isNullOrEmpty(this.txServiceGroup)) {
        	//初始化TM客户端
            TMClient.init(this.applicationId, this.txServiceGroup, accessKey, secretKey);
            if (LOGGER.isInfoEnabled()) {
                LOGGER.info("Transaction Manager Client is initialized. applicationId[{}] txServiceGroup[{}]", this.applicationId, this.txServiceGroup);
            }
			//初始化RM客户端
            RMClient.init(this.applicationId, this.txServiceGroup);
            if (LOGGER.isInfoEnabled()) {
                LOGGER.info("Resource Manager is initialized. applicationId[{}] txServiceGroup[{}]", this.applicationId, this.txServiceGroup);
            }

            if (LOGGER.isInfoEnabled()) {
                LOGGER.info("Global Transaction Clients are initialized. ");
            }

            this.registerSpringShutdownHook();
        } else {
            throw new IllegalArgumentException(String.format("applicationId: %s, txServiceGroup: %s", this.applicationId, this.txServiceGroup));
        }
    }

TM和RM底层都是用的Netty进行的通讯
TM的初始化

    public static void init(String applicationId, String transactionServiceGroup, String accessKey, String secretKey) {
        TmNettyRemotingClient tmNettyRemotingClient = TmNettyRemotingClient.getInstance(applicationId, transactionServiceGroup, accessKey, secretKey);
        tmNettyRemotingClient.init();
    }

RM的初始化

    public static void init(String applicationId, String transactionServiceGroup) {
        RmNettyRemotingClient rmNettyRemotingClient = RmNettyRemotingClient.getInstance(applicationId, transactionServiceGroup);
        rmNettyRemotingClient.setResourceManager(DefaultResourceManager.get());
        rmNettyRemotingClient.setTransactionMessageHandler(DefaultRMHandler.get());
        rmNettyRemotingClient.init();
    }

发送请求的核心方法

在这里插入图片描述

 public Object invoke(final MethodInvocation methodInvocation) throws Throwable {
 		//利用AOP获取代理类
        Class<?> targetClass = methodInvocation.getThis() != null ? AopUtils.getTargetClass(methodInvocation.getThis()) : null;
        //获取代理的方法
        Method specificMethod = ClassUtils.getMostSpecificMethod(methodInvocation.getMethod(), targetClass);
        if (specificMethod != null && !specificMethod.getDeclaringClass().equals(Object.class)) {
            Method method = BridgeMethodResolver.findBridgedMethod(specificMethod);
            //判断此方法是否被@GlobalTranscation修饰
            GlobalTransactional globalTransactionalAnnotation = (GlobalTransactional)this.getAnnotation(method, targetClass, GlobalTransactional.class);
            //判断此方法是否被注解@GlobalLock修饰
            GlobalLock globalLockAnnotation = (GlobalLock)this.getAnnotation(method, targetClass, GlobalLock.class);
            boolean localDisable = this.disable || degradeCheck && degradeNum >= degradeCheckAllowTimes;
            if (!localDisable) {
                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());
                    } else {
                        transactional = this.aspectTransactional;
                    }
	//被@GlobalTranstional修饰的方法进入此方法
                    return this.handleGlobalTransaction(methodInvocation, transactional);
                }
	//被@GlobalLock修饰的方法进入此类
                if (globalLockAnnotation != null) {
                    return this.handleGlobalLock(methodInvocation, globalLockAnnotation);
                }
            }
        }

        return methodInvocation.proceed();
    }

事务的核心方法TransactionalTemplate的excute方法

 public Object execute(TransactionalExecutor business) throws Throwable {
 	//获取事务信息
        TransactionInfo txInfo = business.getTransactionInfo();
        //如果为空直接抛异常
        if (txInfo == null) {
            throw new ShouldNeverHappenException("transactionInfo does not exist");
        } else {
        	//创建或者获取全局事务标志xid
            GlobalTransaction tx = GlobalTransactionContext.getCurrent();
            //获取事务的传播机制
            Propagation propagation = txInfo.getPropagation();
            SuspendedResourcesHolder suspendedResourcesHolder = null;

            try {
                Object var6;
                switch(propagation) {
                case NOT_SUPPORTED:
                    if (this.existingTransaction(tx)) {
                        suspendedResourcesHolder = tx.suspend();
                    }

                    var6 = business.execute();
                    return var6;
                case REQUIRES_NEW:
                    if (this.existingTransaction(tx)) {
                        suspendedResourcesHolder = tx.suspend();
                        tx = GlobalTransactionContext.createNew();
                    }
                    break;
                case SUPPORTS:
                    if (this.notExistingTransaction(tx)) {
                        var6 = business.execute();
                        return var6;
                    }
                case REQUIRED:
                    break;
                case NEVER:
                    if (this.existingTransaction(tx)) {
                        throw new TransactionException(String.format("Existing transaction found for transaction marked with propagation 'never', xid = %s", tx.getXid()));
                    }

                    var6 = business.execute();
                    return var6;
                case MANDATORY:
                    if (this.notExistingTransaction(tx)) {
                        throw new TransactionException("No existing transaction found for transaction marked with propagation 'mandatory'");
                    }
                    break;
                default:
                    throw new TransactionException("Not Supported Propagation:" + propagation);
                }
				//如果事务的xid为空,则重新创建一个新的
                if (tx == null) {
                    tx = GlobalTransactionContext.createNew();
                }
				//获取全局锁配置
                GlobalLockConfig previousConfig = this.replaceGlobalLockConfig(txInfo);

                try {
                	//开启全局事务
                    this.beginTransaction(txInfo, tx);

                    Object rs;
                    Object ex;
                    try {
                        rs = business.execute();
                    } catch (Throwable var17) {
                        ex = var17;
                        this.completeTransactionAfterThrowing(txInfo, tx, var17);
                        throw var17;
                    }

                    this.commitTransaction(tx);
                    ex = rs;
                    return ex;
                } finally {
                    this.resumeGlobalLockConfig(previousConfig);
                    this.triggerAfterCompletion();
                    this.cleanUp();
                }
            } finally {
                if (suspendedResourcesHolder != null) {
                    tx.resume(suspendedResourcesHolder);
                }

            }
        }
    }

客户端开启事务的核心流程

在这里插入图片描述

    private void beginTransaction(TransactionInfo txInfo, GlobalTransaction tx) throws ExecutionException {
        try {
        	//执行开启事务前的操作
            this.triggerBeforeBegin();
            //开启事务
            tx.begin(txInfo.getTimeOut(), txInfo.getName());
            //执行开启事务后的操作
            this.triggerAfterBegin();
        } catch (TransactionException var4) {
            throw new ExecutionException(tx, var4, Code.BeginFailure);
        }
    }
 public void begin(int timeout, String name) throws TransactionException {
 		//判断当前的角色是不是Launcher
        if (this.role != GlobalTransactionRole.Launcher) {
        	//不是判断当前的xid是否为null
            this.assertXIDNotNull();
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Ignore Begin(): just involved in global transaction [{}]", this.xid);
            }

        } else {
        	//不是判断当前的xid是否为null
            this.assertXIDNull();
            String currentXid = RootContext.getXID();
            if (currentXid != null) {
                throw new IllegalStateException("Global transaction already exists, can't begin a new global transaction, currentXid = " + currentXid);
            } else {
            	//开启事务,请求后台服务,返回xid
                this.xid = this.transactionManager.begin((String)null, (String)null, name, timeout);
                //将事务状态置为开启
                this.status = GlobalStatus.Begin;
                //绑定xid
                RootContext.bind(this.xid);
                if (LOGGER.isInfoEnabled()) {
                    LOGGER.info("Begin new global transaction [{}]", this.xid);
                }

            }
        }
    }

服务端分布式事务的处理机制

在这里插入图片描述

 protected void doGlobalBegin(GlobalBeginRequest request, GlobalBeginResponse response, RpcContext rpcContext)
            throws TransactionException {
        /**
         * 调用core.begin开启事务,并持久化
         * 给response设置全局事务编号XID*/
        response.setXid(core.begin(rpcContext.getApplicationId(), rpcContext.getTransactionServiceGroup(),
                request.getTransactionName(), request.getTimeout()));
        if (LOGGER.isInfoEnabled()) {
            LOGGER.info("Begin new global transaction applicationId: {},transactionServiceGroup: {}, transactionName: {},timeout:{},xid:{}",
                    rpcContext.getApplicationId(), rpcContext.getTransactionServiceGroup(), request.getTransactionName(), request.getTimeout(), response.getXid());
        }
    }
    public String begin(String applicationId, String transactionServiceGroup, String name, int timeout)
        throws TransactionException {
        //key1:创建全局会话,这里面已经创建了全局事务Id
        GlobalSession session = GlobalSession.createGlobalSession(applicationId, transactionServiceGroup, name,
            timeout);
        MDC.put(RootContext.MDC_KEY_XID, session.getXid());
        // 添加事务生命周期监听器
        session.addSessionLifecycleListener(SessionHolder.getRootSessionManager());
        //2、 开启事务,这里有事务持久话的一个机制
        session.begin();

        // transaction start event
        //  发送事务开启事件
        MetricsPublisher.postSessionDoingEvent(session, false);
        // 返回全局事务ID
        return session.getXid();
    }
    @Override
    public void begin() throws TransactionException {
        this.status = GlobalStatus.Begin;
        this.beginTime = System.currentTimeMillis();
        this.active = true;
        for (SessionLifecycleListener lifecycleListener : lifecycleListeners) {
            // 在这里处理
            lifecycleListener.onBegin(this);
        }
    }
private void writeSession(LogOperation logOperation, SessionStorable sessionStorable) throws TransactionException {
        // 将事务信息写入数据库
        if (!transactionStoreManager.writeSession(logOperation, sessionStorable)) {
            if (LogOperation.GLOBAL_ADD.equals(logOperation)) {
                throw new GlobalTransactionException(TransactionExceptionCode.FailedWriteSession,
                    "Fail to store global session");
            } else if (LogOperation.GLOBAL_UPDATE.equals(logOperation)) {
                throw new GlobalTransactionException(TransactionExceptionCode.FailedWriteSession,
                    "Fail to update global session");
            } else if (LogOperation.GLOBAL_REMOVE.equals(logOperation)) {
                throw new GlobalTransactionException(TransactionExceptionCode.FailedWriteSession,
                    "Fail to remove global session");
            } else if (LogOperation.BRANCH_ADD.equals(logOperation)) {
                throw new BranchTransactionException(TransactionExceptionCode.FailedWriteSession,
                    "Fail to store branch session");
            } else if (LogOperation.BRANCH_UPDATE.equals(logOperation)) {
                throw new BranchTransactionException(TransactionExceptionCode.FailedWriteSession,
                    "Fail to update branch session");
            } else if (LogOperation.BRANCH_REMOVE.equals(logOperation)) {
                throw new BranchTransactionException(TransactionExceptionCode.FailedWriteSession,
                    "Fail to remove branch session");
            } else {
                throw new BranchTransactionException(TransactionExceptionCode.FailedWriteSession,
                    "Unknown LogOperation:" + logOperation.name());
            }
        }
    }

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

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

相关文章

不同表格式下的小文件治理方式(开源RC file/ORC/Text非事务表、事务表、Holodesk表格式..)

友情链接&#xff1a; 小文件治理系列之为什么会出现小文件问题&#xff0c;小文件过多问题的危害以及不同阶段下的小文件治理最佳解决手段 小文件过多的解决方法&#xff08;不同阶段下的治理手段&#xff0c;SQL端、存储端以及计算端&#xff09; 概览 在前两篇博文中&am…

短信内容关键字被拦截-阿里云通知类短信

通知类短信。地产、留学、招聘、交友、游戏等行业仅支持发送验证码。 注意不要有字眼&#xff0c;替换为相似的词语。否则拦截率很高

Java多线程基础知识-2

线程的3个方法&#xff1a; Thread.sleep()&#xff1a;当前线程睡眠多少毫秒&#xff0c;让给其他线程去执行。 Thread.yield()&#xff1a;当前线程退出一下&#xff0c;进入到等待队列&#xff0c;让其他线程执行&#xff0c;即让出线程一下。 Thread.join()&#xff1a;…

LEVER:根据执行结果来改进语言编码生成的高效方法

译者 | 赵海树 单位 | 东北大学自然语言处理实验室 1.论文介绍 基于大语言模型LLM训练从语言到编码的生成的方向取得了重要进展&#xff0c;基于执行结果的启发式方法以及LLM解码、样本修剪和测试用例重新排序的结合方式成为了当前最为高效的方法。但当前的许多真实世界语言不…

鸿蒙 Web组件的生命周期(api10、11、12)

概述 开发者可以使用Web组件加载本地或者在线网页。 Web组件提供了丰富的组件生命周期回调接口&#xff0c;通过这些回调接口&#xff0c;开发者可以感知Web组件的生命周期状态变化&#xff0c;进行相关的业务处理。 Web组件的状态主要包括&#xff1a;Controller绑定到Web组…

【vue3】for循环多选框勾选必填校验

业务场景&#xff1a; 多选项必选一个&#xff0c;选了的输入框必填 <el-row :gutter"20"><el-col :span"12"><el-form-item label"捆绑终端硬件标识" prop"terminalCodeList"><el-checkbox-groupv-model"…

rmmod无法卸载驱动程序

出现问题&#xff1a; 在加载好驱动程序key_to_led_drv.ko 运行app然后使用ctrlz退出的时候&#xff0c;无法使用rmmod卸载程序 出现问题&#xff1a; 原因进程没有被杀死&#xff1a; 调试过程&#xff1a; 使用lsmod里的信息&#xff1a; used为驱动占用的计数值 当计数…

Java+ffmpeg 合并两个mp4文件

使用ffmpeg测试命令 ffmpeg -i "E:\Monitor\video_20240617_10.mp4" -i "E:\Monitor\video1_20240617_10.mp4" -filter_complex "[0:v][0:a][1:v][1:a]concatn2:v1:a1[v][a]" -map "[v]" -map "[a]" -c:v libx264 -c:a…

数据分析第十一讲:pandas应用入门(六)

pandas应用入门&#xff08;六&#xff09; 我们再来看看Index类型&#xff0c;它为Series和DataFrame对象提供了索引服务&#xff0c;有了索引我们就可以排序数据&#xff08;sort_index方法&#xff09;、对齐数据&#xff08;在运算和合并数据时非常重要&#xff09;并实现…

2024.6.16 机器学习周报

目录 引言 Abstract 文献阅读 1、题目 2、引言 3、创新点 4、匹配问题 5、SuperGlue架构 5.1、注意力图神经网络&#xff08;Attentional Graph Neural Network&#xff09; 5.2、最佳匹配层&#xff08;Optimal matching layer&#xff09; 5.3、损失 6、实验 6.…

裂变客户时代:如何打造口碑传播的良性循环?【以Notion为例】

在当今的商业环境中&#xff0c;客户裂变已经成为品牌增长的重要驱动力。裂变客户时代&#xff0c;意味着每一个满意的客户都有可能成为品牌的传播者&#xff0c;帮助品牌吸引更多的新客户。那么&#xff0c;如何在这个时代打造口碑传播的良性循环呢&#xff1f;Notion是怎么做…

Python 接口自动化测试

一、基础准备 1. 环境搭建 工欲善其事必先利其器&#xff0c;废话不多说。我们先开始搭建环境。 # 创建项目目录mkdir InterfaceTesting# 切换到项目目录下cd InterfaceTesting# 安装虚拟环境创建工具pip install virtualenv# 创建虚拟环境&#xff0c;env代表虚拟环境的名称&…

微信小程序学习(十):生命周期

1、应用生命周期 生命周期说明onLaunch监听小程序初始化&#xff0c;全局只会执行 1 次onShow监听小程序启动或切前台onHide监听小程序切后台 &#x1f517;应用生命周期官方文档 App({/*** 当小程序初始化完成时&#xff0c;会触发 onLaunch&#xff08;全局只触发一次&…

李宏毅深度学习03——神经网络训练不起来怎么办

视频链接 如果Optimization失败的时候&#xff0c;怎么把梯度下降做的更好&#xff1f; 只考虑这种情况&#xff0c;不考虑overfitting 局部最小值&#xff08;local minima&#xff09;和鞍点&#xff08;saddle point&#xff09; 为什么Optimization会失败&#xff1f; …

用AI帮助设计师提升工作效率?

在创意设计的世界里&#xff0c;效率往往意味着灵感与时间的完美结合。设计师们时常面临各种挑战&#xff0c;如何在保证作品质量的同时&#xff0c;又能提升工作效率&#xff1f;这不仅是设计师们思考的问题&#xff0c;也是AI技术正在积极解决的问题。那么&#xff0c;用AI帮…

Unity 工具 之 Azure 微软 【GPT4o】HttpClient 异步流式请求的简单封装

Unity 工具 之 Azure 微软 【GPT4o】HttpClient 异步流式请求的简单封装 目录 Unity 工具 之 Azure 微软 【GPT4o】HttpClient 异步流式请求的简单封装 一、简单介绍 二、实现原理 三、注意实现 四、简单效果预览 五、案例简单实现步骤 六、关键代码 一、简单介绍 Unit…

Spring系统学习 - FactoryBean和基于XML的自动装配

Factory Bean Spring的FactoryBean是一个特殊的Bean&#xff0c;用于创建其他Bean实例。FactoryBean接口定义了一个工厂Bean&#xff0c;该Bean可以用来生成其他Bean的实例。通过实现FactoryBean接口&#xff0c;开发人员可以自定义Bean的创建逻辑&#xff0c;实现更灵活的Bea…

动态竞拍与寄售模式:引领企业增长的新引擎

在当今日新月异的商业环境中&#xff0c;企业的生存与发展不仅需要卓越的产品和服务&#xff0c;更需要紧跟市场脉搏&#xff0c;勇于创新。接下来&#xff0c;我将为您详细介绍一种引领行业新风尚的商业模式——动态竞拍与寄售相结合的模式。这一模式凭借其灵活性和创新性&…

图像处理与视觉感知复习--频率域图像增强图像变换

文章目录 图像变换与信号分解正弦信号与傅里叶级数傅里叶变换离散傅里叶变换(DFT)频率域滤波 图像变换与信号分解 空间域&#xff1a;就是像素域&#xff0c;在空间域的处理是在像素级的处理&#xff0c;如像素级的叠加。 频率域&#xff1a;任何一个波形都可以分解用多个正弦…

【类型转换】C++中char、char*、int、string相互转换函数及string转不同进制数函数

参考资料&#xff1a;cplusplus官方资料strtol 函数用法 总结&#xff1a; 1、这些在做编程题处理输入数据时常用&#xff0c;需要牢记。