当前位置: 首页 > news >正文

技术派全局异常处理

前言

全局的异常处理是Java后端不可或缺的一部分,可以提高代码的健壮性和可维护性。

在我们的开发中,总是难免会碰到一些未经处理的异常,假如没有做全局异常处理,那么我们返回给用户的信息应该是不友好的,很抽象的,用户会认为我们的程序是不安全的。

相反,如果有了全局异常处理,那么我们就可以给用户提供更友好的反馈。

我们甚至可以把全局异常处理写到简历上,比如说你可以这样描述:项目采用了 HandlerExceptionResolver(或者 ControllerAdvice 方案)的全局异常处理策略,提高了代码的健壮性和可维护性,优化了用户体验。

我会结合具体的业务场景给大家一种身临其境的感觉(@),讲一讲HandlerExceptionResolver 和 ControllerAdvice 具体怎么在项目中使用。

业务场景

技术派整合了Redis,比如用户登录的时候会从Redis中获取缓存,那假如我们没有启动Redis服务呢?

然后我们在本地启动技术派的服务端。

然后点击登录 ->一键登录。

然后就会收到这样一条提示信息。

由于我们项目是开源的,所以这里就直接把服务端的信息返回出来,好让大家第一时间辨别出是哪里出了问题,可以及时去调整。

当你看到这样一条错误提示,第一时间就能明白,哦,原来是 Redis 没有启动啊。

在服务器端的控制台面板中(错误堆栈信息中),可以找到对应的错误信恙。

其中 ForumExceptionHandler 就是用来进行全局异常处理的,它是HandlerExceptionResolver 接囗的实现类

HandlerExceptionResolver

HandlerExceptionResolver 是 Spring 提供的一种异常处理机制,它允许我们在应用程序中以统一的方式处理控制器方法引发的异常。

要使用 HandlerExceptionResolver,我们需要创建一个实现该接口的类,并在其中定义如何处理异常。例如:

@Slf4j
@Order(-100)
public class ForumExceptionHandler implements HandlerExceptionResolver {@Overridepublic ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {}

  • @Slf4j 是 lombok 提供的一个日志注解。
  • @0rder 注解用于指定 Spring 中组件的加载顺序。它接受一个整数值,数值越小,组件的优先级越高,加载顺序越靠前。
  • 在 resolveException 方法中,我们可以自定义异常处理逻辑,根据异常类型返回不同的 ModelAndView。

我们来看一下 resolveException 方法中的具体写法:

@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {Status errStatus = buildToastMsg(ex);if (restResponse(request, response)) {// 表示返回json数据格式的异常提示信息if (response.isCommitted()) {// 如果返回已经提交过,直接退出即可return new ModelAndView();}try {response.reset();// 若是rest接口请求异常时,返回json格式的异常数据;而不是专门的500页面response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);response.setHeader("Cache-Control", "no-cache, must-revalidate");response.getWriter().println(JsonUtil.toStr(ResVo.fail(errStatus)));response.getWriter().flush();response.getWriter().close();return new ModelAndView();} catch (Exception e) {throw new RuntimeException(e);}}String view = getErrorPage(errStatus, response);ModelAndView mv = new ModelAndView(view);response.setContentType(MediaType.TEXT_HTML_VALUE);mv.getModel().put("global", SpringUtil.getBean(GlobalInitService.class).globalAttr());mv.getModel().put("res", ResVo.fail(errStatus));mv.getModel().put("toast", JsonUtil.toStr(ResVo.fail(errStatus)));return mv;
}

技术派做了两种处理,一种是REST接口请求的异常,一种是针对普通页请求的异常。

1、如果是 REST 接口请求异常,代码会返回一个 JSON 格式的异常提示信息:

  • 首先检查响应是否已经提交,如果已经提交,则直接返回一个空的 ModelAndView。
  • 如果响应未提交,将重置响应对象,设置响应的内容类型为JSON,并添加相关的响应头。
  • 使用 response.getwriter()将异常状态对象 errStatus 转换为 JSON 格式并写入响应。完成后,返回一个空的 ModelAndView。

②、如果是普通页面请求异常,代码会返回一个包含错误信息的 HTML 页面:

  • 根据异常状态对象 errStatus 和响应对象 response 获取错误页面的视图名称。
  • 创建-个 ModelAndView 对象,并设置视图名称。
  • 设置响应的内容类型为 HTML。
  • 向 ModelAndView 中添加全局属性、错误响应对象以及错误信息(以JSON 格式)
  • 最后返回这个 ModelAndView 对象,用于展示错误页面。

下图是当遇到 404 错误的时候,返回的 404 页面。

其中 buildToastMsg 方法用来对异常进行分类,使用 instanceof 关键字来判断不同类型的异常,添加不同的异常码和提示消息。

private Status buildToastMsg(Exception ex) {
if (ex instanceof ForumException) {return ((ForumException) ex).getStatus();
} else if (ex instanceof AsyncRequestTimeoutException) {return Status.newStatus(StatusEnum.UNEXPECT_ERROR, "超时未登录");
} else if (ex instanceof HttpMediaTypeNotAcceptableException) {return Status.newStatus(StatusEnum.RECORDS_NOT_EXISTS, ExceptionUtils.getStackTrace(ex));
} else if (ex instanceof HttpRequestMethodNotSupportedException || ex instanceof MethodArgumentTypeMismatchException || ex instanceof IOException) {// 请求方法不匹配return Status.newStatus(StatusEnum.ILLEGAL_ARGUMENTS, ExceptionUtils.getStackTrace(ex));
} else if (ex instanceof NestedRuntimeException) {log.error("unexpect NestedRuntimeException error! {}", ReqInfoContext.getReqInfo(), ex);return Status.newStatus(StatusEnum.UNEXPECT_ERROR, ex.getMessage());
} else {log.error("unexpect error! {}", ReqInfoContext.getReqInfo(), ex);return Status.newStatus(StatusEnum.UNEXPECT_ERROR, ExceptionUtils.getStackTrace(ex));
}
}

StatusEnum 中定义了异常码的规范,举几个例子。

/*** 异常码规范:* xxx - xxx - xxx* 业务 - 状态 - code* <p>* 业务取值* - 100 全局* - 200 文章相关* - 300 评论相关* - 400 用户相关* <p>* 状态:基于http status的含义* - 4xx 调用方使用姿势问题* - 5xx 服务内部问题* <p>* code: 具体的业务code*/
@Getter
public enum StatusEnum {SUCCESS(0, "OK"),// -------------------------------- 通用// 全局传参异常ILLEGAL_ARGUMENTS(100_400_001, "参数异常"),ILLEGAL_ARGUMENTS_MIXED(100_400_002, "参数异常:%s"),// 全局权限相关FORBID_ERROR(100_403_001, "无权限"),
}

getErrorPage 方法用于返回不同的错误页面,比如常见的 404 Not Found(请求的资源不存在,服务器无法找到请求的资源)、403 Forbidden(服务器理解请求,但是拒绝处理它,一般是由于权限问题或者访问被拒绝)500 lnternal Server Error(服务器发生了错误,无法完成请求)等。

private String getErrorPage(Status status, HttpServletResponse response) {// 根据异常码解析需要返回的错误页面if (StatusEnum.is5xx(status.getCode())) {response.setStatus(500);return "error/500";} else if (StatusEnum.is403(status.getCode())) {response.setStatus(403);return "error/403";} else {response.setStatus(404);return "error/404";}
}

restResponse 方法用来判断是否是 REST 请求,比如说 admin 后台请求、api数据请求、上传图片等接口

Ajax 请求等,这些请求统一返回 JSON 格式的异常提示信息,否则返回普通的页面格式的异常提示信息。

**
* 后台请求、api数据请求、上传图片等接口,返回json格式的异常提示信息
* 其他异常,返回500的页面
*
* @param request
* @param response
* @return
*/
private boolean restResponse(HttpServletRequest request, HttpServletResponse response) {if (request.getRequestURI().startsWith("/api/admin/") || request.getRequestURI().startsWith("/admin/")) {return true;}if (request.getRequestURI().startsWith("/image/upload")) {return true;}if (response.getContentType() != null && response.getContentType().contains(MediaType.APPLICATION_JSON_VALUE)) {return true;}if (isAjaxRequest(request)) {return true;}// 数据接口请求AntPathMatcher pathMatcher = new AntPathMatcher();if (pathMatcher.match("/**/api/**", request.getRequestURI())) {return true;}return false;
}

再来看一下自定义的异常类 ForumException,非常简单,继承了 RuntimeException。

/*** 业务异常**/
public class ForumException extends RuntimeException {@Getterprivate Status status;public ForumException(Status status) {this.status = status;}public ForumException(int code, String msg) {this.status = Status.newStatus(code, msg);}public ForumException(StatusEnum statusEnum, Object... args) {this.status = Status.newStatus(statusEnum, args);}}

HandlerExceptionResolver 的工作原理主要基于 Spring MVC 的异常处理流程。当一个请求进入 Spring MVC后,它会根据请求信息找到对应的处理器(handler,也就是Controller)。在Controller 执行过程中,如果抛出了异常,Spring MVC 就会启动异常处理流程。

1)异常发生:当 Controller 执行过程中抛出异常,Spring MVC 捕获到这个异常后,会进入异常处理流程

2)查找异常解析器:Spring MVC 会遍历所有已注册的 HandlerExceptionResolver 实现。比如说我们自定义的

ForumExceptionHandler,Spring MVC 本身也提供了一些默认的实现,比如DefaultHandlerExceptionResolver、ExceptionHandlerExceptionResolver.

3)执行异常解析器:对于每个 HandlerExceptionResolver 实现,Spring MVC会调用它的 resolveException方法,并传入请求、响应、处理器和异常对象。如果解析器能处理这个异常,它会返回一个非空的ModelAndView 对象。这个对象封装了异常处理后的视图和模型数据。

4)处理返回结果:当 resolveException 方法返回一个非空的 ModelAndView 对象时,Spring MVC 会将这个对象用于生成最终的响应。可能渲染一个错误视图、设置响应状态码等。如果所有的HandlerExceptionResolver 都无法处理这个异常(即都返回了空的 ModelAndView对象),那么 SpringMVC 会将异常重新抛出,以便其他异常处理器(如 Servet 容器)进行处理。

通过这个流程,HandlerExceptionResolver 能够在 Spring MVC 中统一管理和处理异常。记得在 Spring Boot的启动类中将自定义的 HandlerExceptionResolver 添加到 Spring 配置中。

@Slf4j
@EnableAsync
@EnableScheduling
@EnableCaching
@ServletComponentScan  //与@WebFilter(urlPatterns = "/*", filterName = "reqRecordFilter", asyncSupported = true)注解配套
@SpringBootApplication
public class QuickForumApplication implements WebMvcConfigurer, ApplicationRunner {@Overridepublic void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {resolvers.add(0, new ForumExceptionHandler());}
}

@ControllerAdvice

除了 HandlerExceptionResolver,全局异常还可以采用 @ControllerAdvice 注解的方式。它可以将通用的操作和逻辑抽离出来,避免在每个控制器中重复相同的操作。

第一步,新建一个自定义的异常类 ForumAdviceException。

/*** 业务异常**/
public class ForumAdviceException extends RuntimeException {@Getterprivate Status status;public ForumAdviceException(Status status) {this.status = status;}public ForumAdviceException(int code, String msg) {this.status = Status.newStatus(code, msg);}public ForumAdviceException(StatusEnum statusEnum, Object... args) {this.status = Status.newStatus(statusEnum, args);}}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Status {/*** 业务状态码*/@ApiModelProperty(value = "状态码, 0表示成功返回,其他异常返回", required = true, example = "0")private int code;/*** 描述信息*/@ApiModelProperty(value = "正确返回时为ok,异常时为描述文案", required = true, example = "ok")private String msg;public static Status newStatus(int code, String msg) {return new Status(code, msg);}public static Status newStatus(StatusEnum status, Object... msgs) {String msg;if (msgs.length > 0) {msg = String.format(status.getMsg(), msgs);} else {msg = status.getMsg();}return newStatus(status.getCode(), msg);}
}

第二步,新建一个全局异常控制器 GlobalExceptionHandler,内容如下所示。

@RestControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(value = ForumAdviceException.class)public ResVo<String> handleForumAdviceException(ForumAdviceException e) {return ResVo.fail(e.getStatus());}
}

@RestControllerAdvice 是一个特殊的 @ControllerAdvice 注解,适用于处理 RESTfu API 异常的情况。这意味着它将用于处理来自带有 @RestController 注解的控制器抛出的异常。

此类中定义的方法 handleForumAdviceException 使用 @ExceptionHandler 注解,表示它将处理ForumAdviceException 类型的异常。

第三步,加一个测试的控制器方法 testControllerAdvice。

@RequestMapping(path = "testControllerAdvice")
@ResponseBody
public String testControllerAdvice() {throw new ForumAdviceException(StatusEnum.ILLEGAL_ARGUMENTS_MIXED, "测试ControllerAdvice异常");
}

第四步,如果之前在启动类中注册了 ForumExceptionHandler,此时需要干掉。

第五步,重启启动服务,测试 testControllerAdvice 接口。

接口返回的内容如下所示

两种全局异常处理的优缺点

好,我们来对比一下两种全局异常处理 HandlerExceptionResolver 和 @ControllerAdvice(或@RestControllerAdvice)的优缺点。

1、HandlerExceptionResolver

HandlerExceptionResolver 是一个接口,用于处理由 Controller 抛出的异常,我们可以重写resolveException方法,在其中实现该接口来自定义全局异常处理逻辑。然后在Spring Boot 的启动类中通过

extendHandlerExceptionResolvers 将自定义的 HandlerExceptionResolver 添加到解析器中。

  • 优点:可以更加灵活地处理异常,因为你可以编写任何处理逻辑。
  • 缺点:与其他 Spring MVC 组件的集成不够紧密,需要手动添加和配置,。

2、@ControllerAdvice(或@RestControllerAdvice)

@ControllerAdvice(或 @RestControllerAdvice)是用于定义全局异常处理类的注解。在这个类中,我们可以使用 @ExceptionHandler 注解来处理不同类型的异常。@ControllerAdvice 需要与 @ExceptionHandler 注解一起使用。

  • 优点:更容易实现和集成,只需创建一个带有 @ControllerAdvice(或 @RestControllerAdvice)注解的类,并使用 @ExceptionHandler 注解定义异常处理方法。
  • 缺点:异常处理逻辑可能不如 HandlerxceptionResolver 那么灵活。

我们可以根据具体的需求和使用场景,选择其中之一来实现全局异常处理。另外二者可以共存,

ControllerAddvice优先级比HandlerExceptionResolver高,也可以用@order注解指定优先级。

相关文章:

技术派全局异常处理

前言 全局的异常处理是Java后端不可或缺的一部分&#xff0c;可以提高代码的健壮性和可维护性。 在我们的开发中&#xff0c;总是难免会碰到一些未经处理的异常&#xff0c;假如没有做全局异常处理&#xff0c;那么我们返回给用户的信息应该是不友好的&#xff0c;很抽象的&am…...

对于mysql 故障的定位和排查

故障表现 他的执行时间超过规定的限制&#xff08;比如1000ms&#xff09;CPU使用率高大量业务失败&#xff0c;数据连接异常执行sql越来越慢&#xff0c;失败越来越多 解决方案 定位 应急 故障恢复 定位 查询慢sql的日志查看mysql 的performance schena&#xff08;里面…...

什么是电航空插头插座连接器有什么作用

航空插头概述 定义与功能 航空插头&#xff0c;又称航空连接器&#xff0c;是一种专门用于航空领域的电连接器&#xff0c;因其最初在航空领域得到广泛应用而得名。航空插头的主要功能是实现电源或信号的连接&#xff0c;尤其适用于芯数较多、结构复杂的线束连接&#xff0c;…...

数据挖掘常见算法(分类算法)

K&#xff0d;近邻算法&#xff08;KNN&#xff09; K-近邻分类法的基本思想&#xff1a;通过计算每个训练数据到待分类元组Zu的距离&#xff0c;取和待分类元组距离最近的K个训练数据&#xff0c;K个数据中哪个类别的训练数据占多数&#xff0c;则待分类元组Zu就属于哪个类别…...

【深度学习】调整加/减模型用于体育运动评估

摘要 一种基于因果关系的创新模型&#xff0c;名为调整加/减模型&#xff0c;用于精准量化个人在团队运动中的贡献。该模型基于明确的因果逻辑&#xff0c;将个体运动员的价值定义为&#xff1a;在假设情景下&#xff0c;用一名价值为零的球员替换该球员后&#xff0c;预期比赛…...

重生之算法刷题之路之链表初探(三)

算法刷题之路之链表初探&#xff08;三&#xff09; 今天来学习的算法题是leecode2链表相加&#xff0c;是一道简单的入门题&#xff0c;但是原子在做的时候其实是有些抓耳挠腮&#xff0c;看了官解之后才恍然大悟&#xff01; 条件 项目解释 有题目可以知道&#xff0c;我们需…...

哪吒汽车,正在等待“太乙真人”的拯救

文丨刘俊宏 在360创始人、哪吒汽车股东周鸿祎近日连续且着急的“督战”中&#xff0c;哪吒汽车&#xff08;下简称哪吒&#xff09;终究还是顶不住了。 6月26日&#xff0c;哪吒通过母公司合众新能源在港交所提交了IPO文件&#xff0c;急迫地希望成为第五家登陆港股的造车新势力…...

HDC Cloud 2024 | CodeArts加速软件智能化开发,携手HarmonyOS重塑企业应用创新体验

2024年6月21~23日&#xff0c;华为开发者大会HDC 2024在东莞溪流背坡村隆重举行。期间华为云主办了以“CodeArts加速软件智能化开发&#xff0c;携手HarmonyOS重塑企业应用创新体验”为主题的分论坛。论坛汇聚了各行各业的专家学者、技术领袖和开发者&#xff0c;共同探讨Harmo…...

基于隐马尔可夫模型的股票预测【HMM】

基于机器学习方法的股票预测系列文章目录 一、基于强化学习DQN的股票预测【股票交易】 二、基于CNN的股票预测方法【卷积神经网络】 三、基于隐马尔可夫模型的股票预测【HMM】 文章目录 基于机器学习方法的股票预测系列文章目录一、HMM模型简介&#xff08;1&#xff09;前向后…...

PostgreSQL Replication Slots

一、PostgreSQL的网络测试 安装PostgreSQL客户端 sudo yum install postgresql 进行网络测试主要是验证客户端是否能够连接到远程的PostgreSQL服务器。以下是使用psql命令进行网络测试的基本步骤&#xff1a; 连接到数据库: 使用psql命令连接到远程的PostgreSQL数据库服务器…...

centos7搭建zookeeper 集群 1主2从

centos7搭建zookeeper 集群 准备前提规划防火墙开始搭建集群192.168.83.144上传安装包添加环境变量修改zookeeper 的配置 192.168.83.145 和 192.168.83.146 配置 启动 集群 准备 vm 虚拟机centos7系统zookeeper 安装包FinalShell或者其他shell工具 前提 虚拟机安装好3台cen…...

Arrays.asList 和 java.util.ArrayList 区别

理解 Java 中的 Arrays.asList 和 java.util.ArrayList 的区别 在 Java 编程中&#xff0c;Arrays.asList 方法和 java.util.ArrayList 是两种常用的处理列表数据的方式。虽然它们在功能上看起来相似&#xff0c;但在内部实现和使用上有着本质的不同。本文将探讨这两种方式的区…...

代码随想录-Day44

322. 零钱兑换 给你一个整数数组 coins &#xff0c;表示不同面额的硬币&#xff1b;以及一个整数 amount &#xff0c;表示总金额。 计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额&#xff0c;返回 -1 。 你可以认为每种硬币的数…...

CriticGPT: 用 GPT-4 找出 GPT-4 的错误

CriticGPT 是 OpenAI 发布的一个基于 GPT-4 的模型&#xff0c;它可以帮助我们人类 Review 并纠正 ChatGPT 在生成代码时的错误。使用 CriticGPT 审查代码时&#xff0c;有 60% 的概率生成的代码更好更正确。...

SpringBoot:SpringBoot中调用失败如何重试

一、引言 在实际的应用中&#xff0c;我们经常需要调用第三方API来获取数据或执行某些操作。然而&#xff0c;由于网络不稳定、第三方服务异常等原因&#xff0c;API调用可能会失败。为了提高系统的稳定性和可靠性&#xff0c;我们通常会考虑实现重试机制。 Spring Retry为Spri…...

2024-06-24 问AI: 在大语言模型中,什么是ray?

文心一言 在大语言模型中&#xff0c;Ray是一个开源的统一框架&#xff0c;用于扩展AI和Python应用程序&#xff0c;特别是在机器学习领域。以下是关于Ray的详细解释&#xff1a; 核心功能&#xff1a; Ray为并行处理提供了计算层&#xff0c;使得用户无需成为分布式系统专家…...

仓库管理系统09--修改用户密码

1、添加窗体 2、窗体布局控件 UI设计这块还是传统的表格布局&#xff0c;采用5行2列 3、创建viewmodel 4、前台UI绑定viewmodel 这里要注意属性绑定和命令绑定及命令绑定时传递的参数 <Window x:Class"West.StoreMgr.Windows.EditPasswordWindow"xmlns"http…...

在Spring Data JPA中使用@Query注解

目录 前言示例简单示例只查询部分字段&#xff0c;映射到一个实体类中只查询部分字段时&#xff0c;也可以使用List<Object[]>接收返回值再复杂一些 前言 在以往写过几篇spring data jpa相关的文章&#xff0c;分别是 Spring Data JPA 使用JpaSpecificationExecutor实现…...

【UE5.1】Chaos物理系统基础——01 创建可被破坏的物体

目录 步骤 一、通过笔刷创建静态网格体 二、破裂静态网格体 三、“统一” 多层级破裂 四、“簇” 群集化的破裂 五、几何体集的材质 六、防止几何体集自动破碎 步骤 一、通过笔刷创建静态网格体 1. 可以在Quixel Bridge中下载两个纹理&#xff0c;用于表示石块的内外纹…...

Linux下SUID提权学习 - 从原理到使用

目录 1. 文件权限介绍1.1 suid权限1.2 sgid权限1.3 sticky权限 2. SUID权限3. 设置SUID权限4. SUID提权原理5. SUID提权步骤6. 常用指令的提权方法6.1 nmap6.2 find6.3 vim6.4 bash6.5 less6.6 more6.7 其他命令的提权方法 1. 文件权限介绍 linux的文件有普通权限和特殊权限&a…...

Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误

HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误&#xff0c;它们的含义、原因和解决方法都有显著区别。以下是详细对比&#xff1a; 1. HTTP 406 (Not Acceptable) 含义&#xff1a; 客户端请求的内容类型与服务器支持的内容类型不匹…...

DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径

目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...

Appium+python自动化(十六)- ADB命令

简介 Android 调试桥(adb)是多种用途的工具&#xff0c;该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具&#xff0c;其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利&#xff0c;如安装和调试…...

uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖

在前面的练习中&#xff0c;每个页面需要使用ref&#xff0c;onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入&#xff0c;需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...

LeetCode - 394. 字符串解码

题目 394. 字符串解码 - 力扣&#xff08;LeetCode&#xff09; 思路 使用两个栈&#xff1a;一个存储重复次数&#xff0c;一个存储字符串 遍历输入字符串&#xff1a; 数字处理&#xff1a;遇到数字时&#xff0c;累积计算重复次数左括号处理&#xff1a;保存当前状态&a…...

【第二十一章 SDIO接口(SDIO)】

第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...

HTML 列表、表格、表单

1 列表标签 作用&#xff1a;布局内容排列整齐的区域 列表分类&#xff1a;无序列表、有序列表、定义列表。 例如&#xff1a; 1.1 无序列表 标签&#xff1a;ul 嵌套 li&#xff0c;ul是无序列表&#xff0c;li是列表条目。 注意事项&#xff1a; ul 标签里面只能包裹 li…...

OkHttp 中实现断点续传 demo

在 OkHttp 中实现断点续传主要通过以下步骤完成&#xff0c;核心是利用 HTTP 协议的 Range 请求头指定下载范围&#xff1a; 实现原理 Range 请求头&#xff1a;向服务器请求文件的特定字节范围&#xff08;如 Range: bytes1024-&#xff09; 本地文件记录&#xff1a;保存已…...

如何为服务器生成TLS证书

TLS&#xff08;Transport Layer Security&#xff09;证书是确保网络通信安全的重要手段&#xff0c;它通过加密技术保护传输的数据不被窃听和篡改。在服务器上配置TLS证书&#xff0c;可以使用户通过HTTPS协议安全地访问您的网站。本文将详细介绍如何在服务器上生成一个TLS证…...

Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)

参考官方文档&#xff1a;https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java&#xff08;供 Kotlin 使用&#xff09; 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…...