【Spring】Spring统一功能处理

Spring统一功能处理

  • 拦截器
    • 拦截器
      • 什么是拦截器
      • 拦截器的基本使用
        • 定义拦截器
        • 注册配置拦截器
      • 拦截器详解
        • 拦截器的拦截路径配置
        • 拦截器实现原理
          • 初始化
          • 处理请求
      • 适配器模式
  • 统一数据返回格式
    • 统一数据返回格式快速入门
  • 统一异常处理

拦截器

场景: 我们要对一个网站实现强制登陆的功能,后端根据Session来判断用户是否登录,但是如果我们要这样实现,就需要对每一个接口都增加这样的逻辑处理 此时就比较麻烦

• 需要修改每个接⼝的处理逻辑
• 需要修改每个接⼝的返回结果
• 接⼝定义修改, 前端代码也需要跟着修改

有没有更简单的办法, 统⼀拦截所有的请求, 并进⾏Session校验呢, 这⾥我们学习⼀种新的解决办法: 拦截器

拦截器

什么是拦截器

拦截器是Spring框架提供的核⼼功能之⼀, 主要⽤来拦截⽤⼾的请求, 在指定⽅法前后, 根据业务需要执⾏预先设定的代码

也就是说, 允许开发⼈员提前预定义⼀些逻辑, 在⽤⼾的请求响应前后执⾏. 也可以在⽤⼾请求前阻⽌
其执⾏.
在拦截器当中,开发⼈员可以在应⽤程序中做⼀些通⽤性的操作, ⽐如通过拦截器来拦截前端发来的
请求, 判断Session中是否有登录⽤⼾的信息. 如果有就可以放⾏, 如果没有就进⾏拦截.

拦截器的基本使用

定义拦截器

实现HandleInterceptor接口 重写方法

@Component
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {log.info("LoginInterceptor 目标方法执行前");return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {log.info("LoginInterceptor 目标方法执行后");}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {log.info("LoginInterceptor 视图执行后");}
}

preHandle方法:目标方法执行前执行,返回true 继续执行后续操作,返回false 中断后续操作
postHandle方法:目标方法执行后执行
afterCompletion()⽅法:视图渲染完毕后执⾏,最后执⾏

注册配置拦截器

实现WebMvcConfigurer接口 重写addInterceptors方法

@Configuration
public class WebConfig implements WebMvcConfigurer {//⾃定义的拦截器对象@Autowiredprivate LoginInterceptor loginInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {//注册⾃定义拦截器对象registry.addInterceptor(loginInterceptor).addPathPatterns("/**");//设置拦截器拦截的请求路径}
}

拦截器详解

拦截器的拦截路径配置

拦截路径是指我们定义的这个拦截器, 对哪些请求⽣效.我们在注册配置拦截器的时候, 通过 addPathPatterns()⽅法指定要拦截哪些请求. 也可以通过excludePathPatterns() 指定不拦截哪些请求

@Configuration
public class WebConfig implements WebMvcConfigurer {//自定义拦截器对象@Autowiredprivate LoginInterceptor loginInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {//设置拦截器的请求路径// /**表示拦截所有registry.addInterceptor(loginInterceptor).addPathPatterns("/**").excludePathPatterns("/user/login").excludePathPatterns("/**/*.js").excludePathPatterns("/**/*.css").excludePathPatterns("/**/*.png").excludePathPatterns("/**/*.html");//addPath设置拦截那些请求//excludePath设置不拦截哪些请求}
}

在拦截器中除了可以设置 /** 拦截所有资源外,还有⼀些常⻅拦截路径设置:

拦截路径含义举例
/*一级路径能匹配/user,/book,/login,不能匹配 /user/login
/**任意级路径能匹配/user,/user/login,/user/reg
/book/*/book下的⼀级路径能匹配/book/addBook,不能匹配/book/addBook/1,/book
/book/**/book下的任意级路径能匹配/book,/book/addBook,/book/addBook/2,不能匹配/user/login

添加拦截器后, 执⾏Controller的⽅法之前, 请求会先被拦截器拦截住. 执⾏ preHandle() ⽅法,
这个⽅法需要返回⼀个布尔类型的值.
如果返回true, 就表⽰放⾏本次操作, 继续访问controller中的⽅法.
如果返回false,则不会放⾏(controller中的⽅法也不会执⾏).

controller当中的⽅法执⾏完毕后,再回过来执⾏ postHandle() 这个⽅法以及afterCompletion() ⽅法,执⾏完毕之后,最终给浏览器响应数据.

拦截器实现原理

当Tomcat启动之后, 有⼀个核⼼的类DispatcherServlet, 它来控制程序的执⾏顺序.

所有请求都会先进到DispatcherServlet,执⾏doDispatch 调度⽅法. 如果有拦截器,会先执⾏拦截器preHandle() ⽅法的代码, 如果 preHandle() 返回true, 继续访问controller中的⽅法.controller当中的⽅法执⾏完毕后,再回过来执⾏ postHandle() 和 afterCompletion(),返回给DispatcherServlet, 最终给浏览器响应数据.
在这里插入图片描述

初始化

DispatcherServlet的初始化⽅法 init() 在其⽗类 HttpServletBean 中实现的.

主要作⽤是加载 web.xml 中 DispatcherServlet 的 配置, 并调⽤⼦类的初始化.
在这里插入图片描述

在 HttpServletBean 的 init() 中调⽤了 initServletBean() , 它是在FrameworkServlet 类中实现的, 主要作⽤是建⽴ WebApplicationContext 容器(有时也称上下⽂), 并加载 SpringMVC 配置⽂件中定义的 Bean到该容器中, 最后将该容器添加到 ServletContext 中. 下⾯是initServletBean() 的具体代码:

在这里插入图片描述
初始化web容器的过程中, 会通过onRefresh 来初始化SpringMVC的容器
在这里插入图片描述
在initStrategies()中进⾏9⼤组件的初始化, 如果没有配置相应的组件,就使⽤默认定义的组件(在
DispatcherServlet.properties中有配置默认的策略, ⼤致了解即可

在这里插入图片描述

⽅法initMultipartResolver、initLocaleResolver、initThemeResolver、initRequestToViewNameTranslator、initFlashMapManager的处理⽅式⼏乎都⼀样(1.2.3.7.8,9),从应⽤⽂中取出指定的Bean, 如果没有, 就使⽤默认的.⽅法initHandlerMappings、initHandlerAdapters、initHandlerExceptionResolvers的处理⽅式⼏乎都⼀样(4,5,6)

  1. 初始化处理器映射器HandlerMappings:处理器映射器作⽤,1)通过处理器映射器找到对应的处理器适配器,将请求交给适配器处理;2)缓存每个请求地址URL对应的位置(Controller.xxx⽅法);如果在ApplicationContext发现有HandlerMappings,则从ApplicationContext中获取到所有的HandlerMappings,并进⾏排序;如果在ApplicationContext中没有发现有处理器映射器,则默认BeanNameUrlHandlerMapping作为处理器映射器
  2. 初始化处理器适配器HandlerAdapter:作⽤是通过调⽤具体的⽅法来处理具体的请求;如果在ApplicationContext发现有handlerAdapter,则从ApplicationContext中获取到所有的 HandlerAdapter,并进⾏排序;如果在ApplicationContext中没有发现处理器适配器,则不设置异常处理器,则默认SimpleControllerHandlerAdapter作为处理器适配器
  3. 初始化异常处理器解析器HandlerExceptionResolver:如果在ApplicationContext发现有handlerExceptionResolver,则从ApplicationContext中获取到所有的HandlerExceptionResolver,并进⾏排序;如果在ApplicationContext中没有发现异常处理器解析器,则不设置异常处理器
处理请求
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {HttpServletRequest processedRequest = request;HandlerExecutionChain mappedHandler = null;boolean multipartRequestParsed = false;WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);try {ModelAndView mv = null;Exception dispatchException = null;try {processedRequest = checkMultipart(request);multipartRequestParsed = (processedRequest != request);//1.获取执行链//遍历所有的HandlerMapper 找到与请求对应的HandlermappedHandler = getHandler(processedRequest);if (mappedHandler == null) {noHandlerFound(processedRequest, response);return;}//2. 获取适配器//遍历所有的HandlerAdapter 找到可以处理该Handler的HandlerAdapterHandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());String method = request.getMethod();boolean isGet = HttpMethod.GET.matches(method);if (isGet || HttpMethod.HEAD.matches(method)) {long lastModified = ha.getLastModified(request, mappedHandler.getHandler());if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {return;}}//3. 执行拦截器的preHandler方法if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}//4. 执行目标方法mv = ha.handle(processedRequest, response, mappedHandler.getHandler());if (asyncManager.isConcurrentHandlingStarted()) {return;}applyDefaultViewName(processedRequest, mv);//5. 执行拦截器的postHandle方法mappedHandler.applyPostHandle(processedRequest, response, mv);}catch (Exception ex) {dispatchException = ex;}catch (Throwable err) {// As of 4.3, we're processing Errors thrown from handler methods as well,// making them available for @ExceptionHandler methods and other scenarios.dispatchException = new NestedServletException("Handler dispatch failed", err);}//6.处理视图 处理之后执⾏拦截器afterCompletion⽅法processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);}catch (Exception ex) {//7.执⾏拦截器afterCompletion⽅法triggerAfterCompletion(processedRequest, response, mappedHandler, ex);}catch (Throwable err) {triggerAfterCompletion(processedRequest, response, mappedHandler,new NestedServletException("Handler processing failed", err));}finally {if (asyncManager.isConcurrentHandlingStarted()) {// Instead of postHandle and afterCompletionif (mappedHandler != null) {mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);}}else {// Clean up any resources used by a multipart request.if (multipartRequestParsed) {cleanupMultipart(processedRequest);}}}}

此处最关键的就是3 4 5 点 这里规定了执行目标方法前执行preHandle 执行目标方法之后执行postHandle方法

适配器模式

适配器模式是一种设计模式,用于将一个类的接口转换成客户端所期望的另一个接口。它允许原本不兼容的类能够合作无间。

适配器模式主要包括两个核心角色:目标接口(Target)和适配器(Adapter)。目标接口是客户端所期望的接口,适配器则是将原本不兼容的类转换成目标接口的中间层。

适配器模式可以通过两种方式实现:类适配器和对象适配器。在类适配器中,适配器继承了被适配类,并实现了目标接口。而在对象适配器中,适配器持有一个被适配对象的实例,并实现了目标接口。

使用适配器模式可以有以下几个好处:

  1. 可以让原本不兼容的类能够一起工作,提高代码的复用性。
  2. 可以封装已有的类,对外隐藏底层的实现细节。
  3. 可以在不修改现有代码的情况下引入新的功能。
    总之,适配器模式是一种常用的设计模式,可用于解决不同接口之间不兼容的问题,使得原本无法合作的类能够协同工作。

在这里插入图片描述
HandlerAdapter 在 Spring MVC 中使⽤了适配器模式

HandlerAdapter 主要⽤于⽀持不同类型的处理器(如 Controller、HttpRequestHandler 或者Servlet 等),让它们能够适配统⼀的请求处理流程。这样,Spring MVC 可以通过⼀个统⼀的接⼝来处理来⾃各种处理器的请求

场景: 前⾯学习的slf4j 就使⽤了适配器模式, slf4j提供了⼀系列打印⽇志的api, 底层调⽤的是log4j 或者logback来打⽇志, 我们作为调⽤者, 只需要调⽤slf4j的api就⾏了

//Slf4j接口
interface Slf4jApi{void log(String message);
}//log4j接口
class Log4j{void log4jLog(String message){System.out.println("Log4j打印:" + message);}
}//slf4j和log4j适配器class Slf4jLog4JAdapter implements Slf4jApi{private Log4j log4j;public Slf4jLog4JAdapter(Log4j log4j) {this.log4j = log4j;}@Overridepublic void log(String message) {log4j.log4jLog(message);}
}
public class Slf4jDemo {public static void main(String[] args) {Slf4jApi slf4jApi = new Slf4jLog4JAdapter(new Log4j());slf4jApi.log("使用slf4j打印日志");}
}

在这里插入图片描述
适配器模式应⽤场景
⼀般来说,适配器模式可以看作⼀种"补偿模式",⽤来补救设计上的缺陷. 应⽤这种模式算是"⽆奈之举", 如果在设计初期,我们就能协调规避接⼝不兼容的问题, 就不需要使⽤适配器模式了

所以适配器模式更多的应⽤场景主要是对正在运⾏的代码进⾏改造, 并且希望可以复⽤原有代码实现新的功能. ⽐如版本升级等

统一数据返回格式

强制登录案例中, 我们共做了两部分⼯作

  1. 通过Session来判断⽤⼾是否登录
  2. 对后端返回数据进⾏封装, 告知前端处理的结果

后端统一返回结果

package com.bite.book.model;import com.bite.book.enums.ResultCode;
import lombok.Data;@Data
public class Result<T> {/*** 业务状态码*/private ResultCode code;  //0-成功  -1 失败  -2 未登录/*** 错误信息*/private String errMsg;/*** 数据*/private T data;public static <T> Result<T> success(T data){Result result = new Result();result.setCode(ResultCode.SUCCESS);result.setErrMsg("");result.setData(data);return result;}public static <T> Result<T> fail(String errMsg){Result result = new Result();result.setCode(ResultCode.FAIL);result.setErrMsg(errMsg);result.setData(null);return result;}public static <T> Result<T> fail(String errMsg,Object data){Result result = new Result();result.setCode(ResultCode.FAIL);result.setErrMsg(errMsg);result.setData(data);return result;}public static <T> Result<T> unlogin(){Result result = new Result();result.setCode(ResultCode.UNLOGIN);result.setErrMsg("用户未登录");result.setData(null);return result;}}

后端返回接口

@RequestMapping("/getBookListByPage")public Result getBookListByPage(PageRequest pageRequest, HttpSession session){log.info("查询翻页信息, pageRequest:{}",pageRequest);//校验成功if (pageRequest.getPageSize()<0 || pageRequest.getCurrentPage()<1){return Result.fail("参数校验失败");}PageResult<BookInfo> bookInfoPageResult = null;try {bookInfoPageResult = bookService.selectBookInfoByPage(pageRequest);//此处对返回的数据进行再次封装return Result.success(bookInfoPageResult);}catch (Exception e){log.error("查询翻页信息错误,e:{}",e);return Result.fail(e.getMessage());}}

拦截器帮我们实现了第⼀个功能, 接下来看SpringBoot对第⼆个功能如何⽀持

统一数据返回格式快速入门

统一的数据返回格式使用@ControllerAdviceResponseBodyAdvice 的方式实现

@ControllerAdvice表示控制器通知类

添加类 ResponseAdvice , 实现 ResponseBodyAdvice 接⼝, 并在类上添加 @ControllerAdvice 注解

@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {@Autowired//转jsonprivate ObjectMapper objectMapper;//判断是否要执行beforeBodyWrite方法//ture为执行//false不执行@Overridepublic boolean supports(MethodParameter returnType, Class converterType) {return true;}@SneakyThrows@Overridepublic Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {return Result.success(body);}
}

supports方法: 判断是否要执行beforeBodyWrite方法, true为执行 false不执行, 通过该方法可以选择那些类或者哪些方法的response要进行处理 其他的不处理
beforeBodyWrite方法: 对response方法进行具体操作处理

统一异常处理

统⼀异常处理使⽤的是 @ControllerAdvice + @ExceptionHandler 来实现的,@ControllerAdvice 表⽰控制器通知类, @ExceptionHandler 是异常处理器,两个结合表⽰当出现异常的时候执⾏某个通知,也就是执⾏某个⽅法事件

package com.bite.book.config;import com.bite.book.model.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;@ControllerAdvice
@ResponseBody
@Slf4j
public class ErrorHandler {@ExceptionHandlerpublic Object handler(Exception e){log.info("发生异常 e:{}",e.getMessage());return Result.fail(e.getMessage());}@ExceptionHandlerpublic Object handler(NullPointerException e) {return Result.fail("发⽣NullPointerException:"+e.getMessage());}@ExceptionHandlerpublic Object handler(ArithmeticException e) {return Result.fail("发⽣ArithmeticException:"+e.getMessage());}
}

当有多个异常通知时,匹配顺序为当前类及其⼦类向上依次匹配

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

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

相关文章

ChibiOS简介2/5

ChibiOS简介2/5 1. 源由2. ChibiOS基础知识2/52.4 Chapter 4 - ChibiOS General Architecture2.4.1 The Big Picture&#xff08;总体框图&#xff09;2.4.2 Embedded Components&#xff08;嵌入式组件&#xff09;2.4.3 Application Model&#xff08;应用模型&#xff09;2.…

爬虫解析——Xpath的安装及使用(五)

目录 一、Xpath插件的安装 二、安装 lxml 三、Xpath解析文件 1.解析本地文件 &#xff08;1&#xff09;导入本地文件 &#xff08;2&#xff09;解析本地文件 2.服务器文件解析 &#xff08;1&#xff09;获取网页源码 &#xff08;2&#xff09;解析服务器响应文件 …

力扣373. 查找和最小的 K 对数字

优先队列 思路&#xff1a; 使用下标 (x, y) 标识数值对&#xff0c;x 为第一个数组的下标&#xff0c;y 为第二个数组的下标&#xff1b;所以 k 个数值对 x 的范围属于 [0, min(k, m)]&#xff0c;m 为第一个数组的 size&#xff1b;数值对 (x, y) &#xff0c;那么下一个比其…

TailwindCSS 如何处理RTL布局模式

背景 TikTok作为目前全世界最受欢迎的APP&#xff0c;需要考虑兼容全世界各个地区的本地化语言和阅读习惯。其中对于阿拉伯语、波斯语等语言的阅读书写习惯是从右向左的&#xff0c;在前端有一个专有名字RTL模式&#xff0c;即Right-to-Left。 其中以阿拉伯语作为第一语言的人…

C# 获取windows 系统开关机时间

关机时间&#xff0c;引用&#xff1a;https://www.coder.work/article/1589448 public static DateTime GetLastSystemShutdown() { string sKey "System\CurrentControlSet\Control\Windows"; Microsoft.Win32.RegistryKey key …

建立个人学习观|地铁上的自习室

作者&#xff1a;向知 如果大家有机会来北京&#xff0c;可以来看看工作日早上八九点钟&#xff0c;15 号线从那座叫“顺义”的城市通向“望京”的地铁&#xff0c;你在那上面&#xff0c;能看到明明白白的&#xff0c;人们奔向梦想的模样。 一、地铁上的自习室 我在来北京之前…

华为数据之道学习笔记】3-5 规则数据治理

在业务规则管理方面&#xff0c;华为经常面对“各种业务场景业务规则不同&#xff0c;记不住&#xff0c;找不到”“大量规则在政策、流程等文件中承载&#xff0c;难以遵守”“各国规则均不同&#xff0c;IT能否一国一策、快速上线”等问题。 规则数据是结构化描述业务规则变量…

【算法集训】基础数据结构:三、链表

链表就是将所有数据都用一个链子串起来&#xff0c;其中链表也有多种形式&#xff0c;包含单向链表、双向链表等&#xff1b; 现在毕竟还是基础阶段&#xff0c;就先学习单链表吧&#xff1b; 链表用头结点head表示一整个链表&#xff0c;每个链表的节点包含当前节点的值val和下…

2024 年顶级的 Android 系统修复软件与方法

您是否正在寻找可以修复 PC 上 Android 操作系统的工具&#xff1f;这是我们精选的最好的 Android 系统修复软件&#xff01; Android 是世界著名的智能手机操作系统。全世界有数百万人使用这个操作系统&#xff0c;这使得它安全可靠。然而&#xff0c;这仍然不能使它完美无缺…

048:利用vue-video-player播放m3u8

第048个 查看专栏目录: VUE ------ element UI 专栏目标 在vue和element UI联合技术栈的操控下&#xff0c;本专栏提供行之有效的源代码示例和信息点介绍&#xff0c;做到灵活运用。 &#xff08;1&#xff09;提供vue2的一些基本操作&#xff1a;安装、引用&#xff0c;模板使…

普冉(PUYA)单片机开发笔记(6): 呼吸灯

概述 上一篇的实验中&#xff0c;分别正确地配置了 TIM16 和 TIM1&#xff0c;TIM16 的中断服务程序中每隔 500ms 翻转板载 LED 一次&#xff1b;TIM1 的 CHANNEL_1 用于输出一个固定占空比的 PWM 信号。这一次我们进一小步&#xff1a;使用 TIM16 的中断设置 TIM1 CHANNEL_1 …

MyBatis进阶之分页和延迟加载

文章目录 分页1. RowBounds 分页2. PageHelper 分页3. PageInfo 对象属性描述 延迟加载立即加载激进式延迟加载真-延迟加载 分页 Mybatis 中实现分页功能有 3 种途径&#xff1a; RowBounds 分页&#xff08;不建议使用&#xff09;Example 分页&#xff08;简单情况可用)Pag…

关于对向量检索研究的一些学习资料整理

官方学习资料 主要是的学习资料是&#xff0c; 官方文档 和官方博客。相关文章还是挺多 挺不错的 他们更新也比较及时。有最新的东西 都会更新出来。es scdn官方博客 这里简单列一些&#xff0c;还有一些其他的&#xff0c;大家自己感兴趣去看。 什么是向量数据库 Elasticse…

文件加密软件哪个最好用 好用的文件加密软件推荐

一说到文件加密软件&#xff0c;可能大家都会去搜一些不知名的软件来&#xff0c;但是选择这种加密软件&#xff0c;最好还是要看一些资质的。 资质不好的&#xff0c;可能加密过后你自己也打不开文件&#xff0c;&#xff08;ps&#xff1a;我自己就遇到过这种情况&#xff09…

【华为OD机试python】分苹果【2023 B卷|100分】

【华为OD机试】-真题 !!点这里!! 【华为OD机试】真题考点分类 !!点这里 !! 题目描述 A、B两个人把苹果分为两堆,A希望按照他的计算规则等分苹果, 他的计算规则是按照二进制加法计算,并且不计算进位 12+5=9(1100 + 0101 = 9), B的计算规则是十进制加法,包括正常进位,…

基于Java SSM框架高校校园点餐订餐系统项目【项目源码+论文说明】计算机毕业设计

基于java的SSM框架高校校园点餐订餐系统演示 摘要 21世纪的今天&#xff0c;随着社会的不断发展与进步&#xff0c;人们对于信息科学化的认识&#xff0c;已由低层次向高层次发展&#xff0c;由原来的感性认识向理性认识提高&#xff0c;管理工作的重要性已逐渐被人们所认识&a…

(一)Java 基础语法

目录 一. 前言 二. Hello World 三. Java 语法 3.1. 基本语法 3.2. Java 标识符 3.3. Java 修饰符 3.4. Java 变量 3.5. Java 数组 3.6. Java 枚举 3.7. Java 关键字 3.8. Java 注释 3.9. Java 空行 3.10. Java 继承 3.11. Java 接口&#xff08;interface&#…

Oracle(2-14)User-Managed Incomplete Recovery

文章目录 一、基础知识1、Incomplete Recovery Overview 不完全恢复概述2、Situations Requiring IR 需要不完全恢复的情况3、Types of IR 不完全恢复的类型4、IR Guidelines 不完全恢复指南5、User-Managed Procedures 用户管理程序6、RECOVER Command Overview 恢复命令概述7…

算法训练营Day8(字符串)

344.反转字符串 344. 反转字符串 - 力扣&#xff08;LeetCode&#xff09; class Solution {public void reverseString(char[] s) {for(int i 0,j s.length-1;i< s.length/2 ; i,j--){swap(s,i,j);}}public void swap(char[] s,int i,int j ){char temp s[i];s[i] s[j]…

Python数据科学视频讲解:Python注释

2.3 Python注释 视频为《Python数据科学应用从入门到精通》张甜 杨维忠 清华大学出版社一书的随书赠送视频讲解2.3节内容。本书已正式出版上市&#xff0c;当当、京东、淘宝等平台热销中&#xff0c;搜索书名即可。内容涵盖数据科学应用的全流程&#xff0c;包括数据科学应用和…