SpringBoot整合Retry详细教程
问题背景
在现代的分布式系统中,服务间的调用往往需要处理各种网络异常、超时等问题。重试机制是一种常见的解决策略,它允许应用程序在网络故障或临时性错误后自动重新尝试失败的操作。
Spring Boot提供了灵活的方式来集成重试机制,这可以通过使用Spring Retry模块来实现。本文将通过一个具体的使用场景来详细介绍如何在Spring Boot应用中集成和使用Spring Retry技术。
场景描述
假如我们正在开发一个OMS系统(订单管理系统),其中一个关键服务 OrderService 负责订单创建和调用WMS服务扣减库存API 。由于网络不稳定或外部 API 可能暂时不可用,我们需要在这些情况下实现重试机制,以确保请求的成功率和系统的稳定性。
实现步骤
1. 添加依赖
首先,在你的 pom.xml 文件中添加 Spring Retry 和 AOP 的依赖:
<dependencies><!-- Spring Boot Starter Web --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- Spring Retry --><dependency><groupId>org.springframework.retry</groupId><artifactId>spring-retry</artifactId></dependency><!-- Spring Boot Starter AOP --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency></dependencies>
2. 创建配置类
创建一个配置类来配置重试模板:
package com.zlp.retry.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.retry.backoff.ExponentialBackOffPolicy;
import org.springframework.retry.policy.SimpleRetryPolicy;
import org.springframework.retry.support.RetryTemplate;
/*** 全局重试配置*/
@Configuration
public class CustomRetryConfig {/**** 这段代码定义了一个 `CustomRetryConfig` 类,其中包含一个 `retryTemplate` 方法。该方法用于创建并配置一个 `RetryTemplate` 对象,该对象用于处理重试逻辑。** 1. 创建 `RetryTemplate` 实例**:创建一个 `RetryTemplate` 对象。* 2. 设置重试策略:使用 `SimpleRetryPolicy` 设置最大重试次数为5次。* 3. 设置延迟策略:使用 `ExponentialBackOffPolicy` 设置初始延迟时间为1000毫秒,每次重试间隔时间乘以2。* 4. 应用策略:将重试策略和延迟策略应用到 `RetryTemplate` 对象。*/@Beanpublic RetryTemplate retryTemplate() {RetryTemplate template = new RetryTemplate();// 设置重试策略SimpleRetryPolicy policy = new SimpleRetryPolicy();policy.setMaxAttempts(5);// 设置延迟策略ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();backOffPolicy.setInitialInterval(1000);backOffPolicy.setMultiplier(2.0);template.setRetryPolicy(policy);template.setBackOffPolicy(backOffPolicy);return template;}
}
3. 创建服务类
创建一个服务类 OrderService和接口实现类OrderServiceImpl,并在需要重试的方法上使用 @Retryable 注解:
/*** @Classname OrderService* @Date 2024/11/18 21:03* @Created by ZouLiPing*/
public interface OrderService {/*** 创建订单* @param createOrderReq* @return*/String createOrder(CreateOrderReq createOrderReq);
}
package com.zlp.retry.service.impl;import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.zlp.retry.dto.CreateOrderReq;
import com.zlp.retry.service.OrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;import java.util.UUID;/*** @Classname OrderServiceImpl* @Date 2024/11/18 21:06* @Created by ZouLiPing*/
@Service
@Slf4j(topic = "OrderServiceImpl")
public class OrderServiceImpl implements OrderService {@Override@Retryable(value = {Exception.class},maxAttempts = 4, backoff = @Backoff(delay = 3000))public String createOrder(CreateOrderReq createOrderReq) {log.info("createOrder.req createOrderReq:{}", JSON.toJSONString(createOrderReq));try {log.info("createOrder.deductStock.调用时间={}", DateUtil.formatDateTime(DateUtil.date()));// 扣减库存服务this.deductStock(createOrderReq);} catch (Exception e) {throw new RuntimeException(e);}return UUID.randomUUID().toString();}/*** 模拟扣减库存*/private void deductStock(CreateOrderReq createOrderReq) {throw new RuntimeException("库存扣减失败");}/*** 当重试四次仍未能成功创建订单时调用此方法进行最终处理** @param ex 异常对象,包含重试失败的原因* @param createOrderReq 创建订单的请求对象,包含订单相关信息* @return 返回处理结果,此处返回"fail"表示最终失败*/@Recoverpublic String recover(Exception ex, CreateOrderReq createOrderReq) {// 记录重试四次后仍失败的日志,包括异常信息和订单请求内容log.error("recover.resp.重试四次还是失败.error:{},createOrderReq:{}",ex.getMessage(),JSON.toJSONString(createOrderReq));// 处理最终失败的情况// 可以记录日志,或者是投递MQ,采用最终一致性的方式处理return "fail";}
}
4. 启用重试功能
在主配置类或启动类上添加 @EnableRetry 注解,以启用重试功能:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.retry.annotation.EnableRetry;@SpringBootApplication
@EnableRetry
public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);}
}
5. 验证重试方法
@RestController
@RequiredArgsConstructor
public class RetryController {private final OrderService orderService;@GetMapping("getRetry")public String retry(){CreateOrderReq createOrderReq = new CreateOrderReq();createOrderReq.setOrderId(UUID.randomUUID().toString());createOrderReq.setProductId("SKU001");createOrderReq.setCount(10);createOrderReq.setMoney(100);return orderService.createOrder(createOrderReq);}
}
6.执行操作说明
- 添加依赖:引入了 Spring Retry 和 AOP 的依赖,以便使用重试功能。
- 配置重试模板:创建了一个配置类
CustomRetryConfig,配置了重试策略,设置最大重试次数为5次。 - 创建服务类:在
OrderServiceImpl类中,使用@Retryable注解标记了createOrder方法,指定了当发生Exception时进行重试,最大重试次数为4次,每次重试间隔3秒。同时,使用@Recover注解标记了recover方法,当所有重试都失败后,会调用这个方法。 - 启用重试功能:在主配置类或启动类上添加
@EnableRetry注解,以启用重试功能。
Retry执行流程
Retry整体流程图
打印日志
从日志分析每隔3秒钟会重试一次,直到到达设置最大重试次数,会调用功<font style="color:#DF2A3F;">recover</font>方法中

7.结论
通过以上步骤,我们成功地在 Spring Boot应用中集成了 Spring Retry 技术,实现了服务调用的重试机制。这不仅提高了系统的健壮性和稳定性,还减少了因网络问题或外部服务暂时不可用导致的请求失败。希望本文对你理解和应用 Spring Boot 中的重试技术有所帮助。
Retry配置的优先级规则
- 方法级别配置:如果某个配置在方法上定义了,则该方法上的配置会覆盖类级别的配置和全局配置。
- 类级别配置:如果某个配置在类上定义了,并且该类的方法没有单独定义配置,则使用类级别的配置。
- 全局配置:如果没有在方法或类上定义配置,则使用全局配置。
下面通过一个具体的例子来展示这些优先级规则。假设我们有一个服务类 <font style="color:rgb(44, 44, 54);">MyService</font>,其中包含一些方法,并且我们在不同的层次上进行了重试策略的配置。
示例代码
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;@Service
@Retryable(value = {RuntimeException.class},maxAttempts = 3,backoff = @Backoff(delay = 1000) // 类级别的配置
)
public class MyService {@Retryable(value = {RuntimeException.class},maxAttempts = 5,backoff = @Backoff(delay = 500) // 方法级别的配置)public void retryableMethodWithSpecificConfig() {System.out.println("Retrying with specific config...");throw new RuntimeException("Simulated exception");}@Retryable(value = {RuntimeException.class})public void retryableMethodWithoutSpecificDelay() {System.out.println("Retrying without specific delay...");throw new RuntimeException("Simulated exception");}public void nonRetryableMethod() {System.out.println("This method does not retry.");throw new RuntimeException("Simulated exception");}
}
解释
- retryableMethodWithSpecificConfig
- 方法级别配置:
<font style="color:rgb(44, 44, 54);">maxAttempts = 5</font><font style="color:rgb(44, 44, 54);">backoff.delay = 500</font>
- 这些配置会覆盖类级别的配置。
- 方法级别配置:
- retryableMethodWithoutSpecificDelay
- 方法级别配置:
<font style="color:rgb(44, 44, 54);">maxAttempts = 3</font>(继承自类级别)<font style="color:rgb(44, 44, 54);">backoff.delay = 1000</font>(继承自类级别)
- 这些配置继承自类级别的配置。
- 方法级别配置:
- nonRetryableMethod
- 该方法没有使用
<font style="color:rgb(44, 44, 54);">@Retryable</font>注解,因此不会进行重试。
- 该方法没有使用
全局配置示例
为了进一步说明全局配置的优先级,我们可以配置一个全局的重试模板。
配置类
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.retry.backoff.FixedBackOffPolicy;
import org.springframework.retry.policy.SimpleRetryPolicy;
import org.springframework.retry.support.RetryTemplate;@Configuration
public class RetryConfig {@Beanpublic RetryTemplate retryTemplate() {RetryTemplate template = new RetryTemplate();SimpleRetryPolicy policy = new SimpleRetryPolicy();policy.setMaxAttempts(4);template.setRetryPolicy(policy);FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();fixedBackOffPolicy.setBackOffPeriod(750L);template.setBackOffPolicy(fixedBackOffPolicy);return template;}
}
使用全局配置的服务类
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;@Service
public class AnotherService {@Autowiredprivate RetryTemplate retryTemplate;@Retryable(value = {RuntimeException.class},maxAttempts = 6, // 方法级别的配置backoff = @Backoff(delay = 300) // 方法级别的配置)public void retryableMethodWithGlobalAndLocalConfig() {System.out.println("Retrying with global and local config...");throw new RuntimeException("Simulated exception");}@Retryable(value = {RuntimeException.class})public void retryableMethodWithOnlyGlobalConfig() {System.out.println("Retrying with only global config...");throw new RuntimeException("Simulated exception");}
}
解释
- retryableMethodWithGlobalAndLocalConfig
- 方法级别配置:
<font style="color:rgb(44, 44, 54);">maxAttempts = 6</font><font style="color:rgb(44, 44, 54);">backoff.delay = 300</font>
- 这些配置会覆盖全局配置。
- 方法级别配置:
- retryableMethodWithOnlyGlobalConfig
- 全局配置:
<font style="color:rgb(44, 44, 54);">maxAttempts = 4</font><font style="color:rgb(44, 44, 54);">backoff.delay = 750</font>
- 这些配置继承自全局配置。
- 全局配置:
总结一下,配置的优先级从高到低依次是:
- 方法级别配置
- 类级别配置
- 全局配置
希望这个示例能帮助你理解不同层次配置的优先级。
什么样的场景不适合Retry
在使用重试机制时,确实有一些场景不适合应用重试策略。了解这些场景有助于避免不必要的重试操作,从而提高系统的性能和稳定性。以下是几种不适合使用重试机制的常见场景:
- 幂等性不可保证的操作
- 解释:如果一个操作不是幂等的(即多次执行会产生不同的结果),那么重试可能导致数据不一致或其他问题。
- 示例:插入数据库记录的操作通常不是幂等的,因为重复插入会导致重复的数据。
- 长时间运行的操作
- 解释:对于耗时较长的操作,频繁重试可能会导致系统资源被大量占用,影响其他任务的执行。
- 示例:批量处理大数据集、长时间计算的任务。
- 外部依赖不稳定但无法恢复
- 解释:某些外部服务或API可能存在根本性的故障,无法通过简单的重试解决。在这种情况下,重试只会浪费资源。
- 示例:调用第三方支付接口,如果返回的是明确的失败状态码(如账户余额不足),则不应该重试。
- 网络超时且无可用备用路径
- 解释:在网络请求超时时,如果没有任何备用路径或解决方案,重试可能仍然会失败。
- 示例:尝试连接到某个特定IP地址的服务,如果该地址一直不通,则重试没有意义。
- 用户交互过程中需要立即反馈的操作
- 解释:在用户等待响应的过程中,长时间的重试可能导致用户体验不佳。
- 示例:提交表单后立即显示成功消息,如果在此期间发生错误并进行重试,用户可能会感到困惑。
- 涉及敏感信息的操作
- 解释:对于涉及敏感信息的操作(如密码修改、资金转账),重试可能会导致敏感信息泄露或重复操作。
- 示例:更新用户的银行账户信息,一旦确认操作完成,不应再进行重试。
- 事务边界内的操作
- 解释:在事务边界内,重试可能会导致事务冲突或回滚,增加复杂性。
- 示例:在一个复杂的数据库事务中,部分操作失败后进行重试可能导致整个事务失败。
- 已知的永久性错误
- 解释:如果能够明确判断出错误是永久性的(如配置错误、代码bug),重试不会解决问题。
- 示例:尝试读取不存在的文件,这种错误通常是永久性的。
- 高并发环境下的写操作
- 解释:在高并发环境下,频繁的重试可能会加剧数据库负载,导致更多的锁竞争和死锁。
- 示例:在电商网站的下单高峰期,对库存的减少操作不宜频繁重试。
示例代码
为了更好地理解这些原则,下面是一个简单的示例,展示了如何在Spring Boot中使用<font style="color:rgb(44, 44, 54);">@Retryable</font>注解,并根据上述原则决定哪些操作适合重试。
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;@Service
public class MyService {// 适合重试的操作:幂等性强、短时间操作、可恢复的错误@Retryable(value = {RuntimeException.class},maxAttempts = 3,backoff = @Backoff(delay = 1000))public void retryableFetchData() {System.out.println("Fetching data...");// 模拟网络请求或短暂的外部服务调用if (Math.random() > 0.5) {throw new RuntimeException("Simulated transient network error");}}// 不适合重试的操作:幂等性不可保证、长时间运行public void nonRetryableLongRunningTask() {System.out.println("Starting long-running task...");try {Thread.sleep(10000); // 模拟长时间运行的任务} catch (InterruptedException e) {Thread.currentThread().interrupt();}System.out.println("Long-running task completed.");}// 不适合重试的操作:涉及敏感信息public void updateSensitiveInformation(String sensitiveData) {System.out.println("Updating sensitive information...");// 这里假设更新操作不是幂等的,也不应该重试throw new RuntimeException("Simulated failure in updating sensitive information");}// 不适合重试的操作:已知的永久性错误public void fetchDataFromNonExistentResource() {System.out.println("Fetching data from a non-existent resource...");throw new RuntimeException("Permanent error: Resource not found");}
}
解释
- retryableFetchData
- 适用条件:
- 幂等性强:每次请求的结果相同。
- 短时间操作:模拟网络请求或短暂的外部服务调用。
- 可恢复的错误:模拟暂时的网络错误。
- 重试策略:
- 最大重试次数为3次。
- 每次重试间隔1秒。
- 适用条件:
- nonRetryableLongRunningTask
- 不适用原因:
- 长时间运行:模拟长时间运行的任务。
- 重试可能导致资源过度消耗。
- 不适用原因:
- updateSensitiveInformation
- 不适用原因:
- 涉及敏感信息:更新操作不是幂等的,也不应该重试。
- 重试可能导致数据不一致或其他安全问题。
- 不适用原因:
- fetchDataFromNonExistentResource
- 不适用原因:
- 已知的永久性错误:资源不存在,重试不会解决问题。
- 不适用原因:
通过这些示例,你可以更好地理解哪些操作适合重试以及为什么某些操作不适合重试。
相关文章:
SpringBoot整合Retry详细教程
问题背景 在现代的分布式系统中,服务间的调用往往需要处理各种网络异常、超时等问题。重试机制是一种常见的解决策略,它允许应用程序在网络故障或临时性错误后自动重新尝试失败的操作。Spring Boot 提供了灵活的方式来集成重试机制,这可以通过…...
JS API事件监听(绑定)
事件监听 语法 元素对象.addEventListener(事件监听,要执行的函数) 事件监听三要素 事件源:那个dom元素被事件触发了,要获取dom元素 事件类型:用说明方式触发,比如鼠标单击click、鼠标经过mouseover等 事件调用的函数&#x…...
ceph手动部署
ceph手动部署 一、 节点规划 主机名IP地址角色ceph01.example.com172.18.0.10/24mon、mgr、osd、mds、rgwceph02.example.com172.18.0.20/24mon、mgr、osd、mds、rgwceph03.example.com172.18.0.30/24mon、mgr、osd、mds、rgw 操作系统版本: Rocky Linux release …...
superset load_examples加载失败解决方法
如果在执行load_examples命令后,出现上方图片情况,或是相似报错(url error\connection error),大概率原因是python程序请求github数据,无法访问. 因此我们可以将数据下载在本地来解决. 1.下载zip压缩文件,存放到本地 官方示例地址:GitHub - apache-superset/examples-data …...
wareshark分析mysql协议的数据包
使用wareshark 分析mysql协议的数据包,是每个dba都应该掌握的技能,掌握以后,就可以通过tcpdump抓包分析,得到连接报错的信息了。 tcpdump抓包命令: tcpdump -nn -i bond0 dst 10.21.6.72 and port 4002 -w 1129_tcpdu…...
HarmonyOS4+NEXT星河版入门与项目实战(25)------UIAbility启动模式(文档编辑案例)
文章目录 1、启动模式2、Specified启动模式实现步骤3、文档编辑案例1、文件创建2代码实现3、Statge 创建4、添加配置1、启动模式 Singleton启动模式: 每个 UIAbility 只存在一个实例,是默认的启动模式,任务列表中只会存在一个相同的 UIAbilityStandard启动模式: 每次启动 U…...
webpack 项目访问静态资源
使用 webpack dev serve 启动 react 项目后,发现无法使用 http://localhost:8080/1.png 访问到项目的 /static 目录下的 1.png 文件。我的 webpack-dev.js 配置如下: const webpack require(webpack) const webpackMerge require(webpack-merge) cons…...
UNION和UNION ALL区别
文章目录 结果集的处理方式:对重复记录的处理:排序处理:执行效率: UNION和UNION ALL的主要区别在于结果集的处理方式、对重复记录的处理、排序处理以及执行效率。 结果集的处理方式: UNION…...
Rook入门:打造云原生Ceph存储的全面学习路径(下)
文章目录 六.Rook部署云原生CephFS文件系统6.1 部署cephfs storageclass6.2 创建容器所需cephfs文件系统6.3创建容器pod使用rook-cephfs提供pvc6.4 查看pod是否使用rook-cephfs 七.Ceph Dashboard界面7.1 启用dashboard开关7.2 ceph-dashboard配置外部访问7.3 Dashboard web ad…...
RabbitMQ消息可靠性保证机制6--可靠性分析
在使用消息中间件的过程中,难免会出现消息错误或者消息丢失等异常情况。这个时候就需要有一个良好的机制来跟踪记录消息的过程(轨迹溯源),帮助我们排查问题。 在RabbitMQ中可以使用Firehose实现消息的跟踪,Firehose可…...
k8s容器存储接口 CSI 相关知识
容器存储接口 CSI 相关知识 参考: https://blog.csdn.net/lovely_nn/article/details/122880876 https://developer.aliyun.com/article/783464 https://www.cnblogs.com/varden/p/15139819.html存储商需实现 CSI 插件的 NodeGetVolumeStats 接口,Kube…...
jmeter基础_打开1个jmeter脚本(.jmx文件)
课程大纲 方法1.菜单栏“打开” 菜单栏“文件” - “打开” (或快捷键,mac为“⌘ O”),打开文件选择窗口 - 选择脚本文件,点击“open”,即可打开脚本。 方法2.工具栏“打开”图标 工具栏点击“打开”图标&…...
Linux---对时/定时服务
文章目录 目录 文章目录 前言 一.对时服务 服务端配置 客户端配置 二.定时服务 单次定时任务 循环定时任务 前言 在当今信息化高速发展的时代,时间的准确性和任务的定时执行对于各种系统和服务来说至关重要。Linux操作系统,凭借其强大的功能和灵活的…...
Agent
Agent核心 1、自主性 2、交互性 3、适应性 4、目的性 ReAct Reasoning and Acting范式 模型的推理过程分为 推理 Reason 和行动 Action 两个步骤,交替执行,直至获得最终结果。 推理 Reason 生成分析步骤,解释当前任务的上下文或状态…...
Oracle 数据库执行增删改查命令的原理与过程
摘要: 本文深入探讨当向 Oracle 数据库发送一个增删改查(CRUD)命令时,数据库内部的执行机制与详细过程。从用户发起命令开始,逐步剖析命令在 Oracle 数据库体系结构各组件中的流转、解析、优化以及执行路径,…...
HBase难点
查询优化 一次Scan会返回大量数据,客户端向HBase发送一次Scan请求,实际上并不会将所有数据加载到本地,而是通过多次RPC请求进行加载,防止客户端OOM。禁止缓存优化:批量读取数据时会全表扫描一次业务表,这种…...
Y20030023 PHP+thinkphp+MYSQL+LW+基于PHP的健身房管理系统的设计与实现 源代码 配置 初稿
基于PHP的健身房管理系统 1.项目摘要2. 系统开发的背景及意义3.项目功能4.界面展示5.源码获取 1.项目摘要 近年来,随着社会发展和科技进步,人们越来越重视健康养生并关注电子商务对日常交流方式的影响。随着健身行业消费人群的增加,竞争变得…...
mongodb下载与使用
下载地址: Install MongoDB Community Kubernetes Operator | MongoDB 1、安装MongoDB (5.0.30) 将压缩包移动到C:\Program Files 下,然后解压创建C:\data\db目录,mongodb 会将数据默认保存在这个文件夹以mongodb 中 bin目录作为工作目录&…...
【Linux基础】Linux基本指令
目录 1、pwd 指令 2、clear 指令 3、ls 指令 1、ls 显示当前路径下的文件或者目录名称 2、ls -l 显示当前路径下的文件或者目录的更详细的属性信息 3、ls -a :显示所有文件,包括隐藏文件 4、ls -d 将目录像文件一样显示,而不是显示其…...
【RISC-V CPU debug 专栏 3 -- Debugging RISC-V Cores】
文章目录 RISC-V 调试规范开源与多样性挑战调试规范的重要性外部调试支持的主要组件调试功能Lauterbach 的贡献RISC-V 调试规范 调试 RISC-V 内核涉及许多独特的挑战,这是由 RISC-V 的开源特性和多样化的生态系统所决定的。为了避免专有调试接口的泛滥,RISC-V 基金会内的工作…...
利用最小二乘法找圆心和半径
#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...
日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻
在如今就业市场竞争日益激烈的背景下,越来越多的求职者将目光投向了日本及中日双语岗位。但是,一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧?面对生疏的日语交流环境,即便提前恶补了…...
CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型
CVPR 2025 | MIMO:支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题:MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者:Yanyuan Chen, Dexuan Xu, Yu Hu…...
【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)
可以使用Sqliteviz这个网站免费编写sql语句,它能够让用户直接在浏览器内练习SQL的语法,不需要安装任何软件。 链接如下: sqliteviz 注意: 在转写SQL语法时,关键字之间有一个特定的顺序,这个顺序会影响到…...
相机从app启动流程
一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...
C# 类和继承(抽象类)
抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...
SpringTask-03.入门案例
一.入门案例 启动类: package com.sky;import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCach…...
Go 并发编程基础:通道(Channel)的使用
在 Go 中,Channel 是 Goroutine 之间通信的核心机制。它提供了一个线程安全的通信方式,用于在多个 Goroutine 之间传递数据,从而实现高效的并发编程。 本章将介绍 Channel 的基本概念、用法、缓冲、关闭机制以及 select 的使用。 一、Channel…...
CRMEB 中 PHP 短信扩展开发:涵盖一号通、阿里云、腾讯云、创蓝
目前已有一号通短信、阿里云短信、腾讯云短信扩展 扩展入口文件 文件目录 crmeb\services\sms\Sms.php 默认驱动类型为:一号通 namespace crmeb\services\sms;use crmeb\basic\BaseManager; use crmeb\services\AccessTokenServeService; use crmeb\services\sms\…...
免费数学几何作图web平台
光锐软件免费数学工具,maths,数学制图,数学作图,几何作图,几何,AR开发,AR教育,增强现实,软件公司,XR,MR,VR,虚拟仿真,虚拟现实,混合现实,教育科技产品,职业模拟培训,高保真VR场景,结构互动课件,元宇宙http://xaglare.c…...
