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

SpringDoc【使用详解】

SpringDoc使用详解

  • 一、何为SpringDoc
  • 二、概念解释
  • 三、SpringDoc使用
    • 2.1简单集成
    • 2.2 配置SpringDoc
      • 2.2.1 yml方式配置
      • 2.2.2配置文档信息
    • 2.3配置文档分组
    • 2.4使用注解
      • 2.4.1 @Tag
      • 2.4.2 @Operation
      • 2.4.3 @Schema
      • 2.4.4 @NotNull
      • 2.4.5 @Parameter
      • 2.4.6 @Parameters
      • 2.4.7 @ApiResponses 和@ApiResponse
  • 四、认证授权
    • 3.1无需认证
    • 3.2需要认证
  • 五、SpringDoc整合Knife4j
    • 1.前世今生
    • 2.Spring Boot版本兼容性
    • 3.Spring Boot 3
    • 4.Spring Boot 2

一、何为SpringDoc

SpringDoc是一个用来自动生成API文档的库。它是基于SpringBoot项目的,遵循OpenAPI3(一个组织规定的规范)规范。它是通过检查我们运行中的程序,推断出基于Spring配置、类结构和各种注解的API语义,从而自动生成JSON、YAML和HTML格式的接口文档。
而我们不得不提的就是Swagger。Swagger是一个公司的开源项目,将自己的API设计贡献给了OpenAPI并由其标准化。在SpringDoc之前我们还可以使用Springfox,和SpringDoc一样是一个用于生成API文档的库,2020年起不再更新。
SpringDoc官网

二、概念解释

谈到API文档,那就绕不开大名鼎鼎的Swagger,但是你是否还听说过:OpenAPI,Springfox,Springdoc?你第一次看到这些脑瓜子是不是嗡嗡的?

  • OpenAPI
    是一个组织(OpenAPI Initiative),他们指定了一个如何描述HTTP API的规范(OpenAPI Specification)。既然是规范,那么谁想实现都可以,只要符合规范即可。

  • Swagger
    它是SmartBear这个公司的一个开源项目,里面提供了一系列工具,包括著名的 swagger-ui。swagger是早于OpenApi的,某一天swagger将自己的API设计贡献给了OpenApi,然后由其标准化了。

  • Springfox
    是Spring生态的一个开源库,是Swagger与OpenApi规范的具体实现。我们使用它就可以在spring中生成API文档。以前基本上是行业标准,目前最新版本可以支持 Swagger2, Swagger3 以及 OpenAPI3 三种格式。但是其从 2020年7月14号就不再更新了,不支持springboot3,所以业界都在不断的转向我们今天要谈论的另一个库Springdoc,新项目就不要用了。

  • Springdoc
    算是后起之秀,带着继任Springfox的使命而来。其支持OpenApi规范,支持Springboot3,我们的新项目都应该使用这个。

三、SpringDoc使用

我们可以在springboot中使用SpringDoc来生成API文档,详情可以参考官网,下面我们来简单的实践一下。

2.1简单集成

在springboot中使用springdoc起步非常容易,只需要引入其starter即可

<dependency><groupId>org.springdoc</groupId><artifactId>springdoc-openapi-starter-webmvc-ui</artifactId><version>2.8.6</version></dependency>

运行后访问下面的链接即可

http://server:port/context-path/swagger-ui.html

例如

http://localhost:8080/swagger-ui.html

例如我们有如下代码:

@RestController
@RequestMapping("/api/programmer")
public class ProgrammerController {@PostMapping()public Programmer createProgrammer(@RequestBody CreateProgrammerRequest request) {return new Programmer(1, request.getAge(), request.getProgrammingLang());}@GetMapping("/{id}")public Programmer getProgrammer(@PathVariable Integer id) {return new Programmer(1, 35, List.of("Java,Python,SQL"));}
}

添加依赖运行后访问http://localhost:8080/swagger-ui.html后结果如下
在这里插入图片描述
是不是特牛逼?当然springdoc的集成不可能就这点东西,不然也没有这篇文章了,咱接着往下看。苦于网上的一些教程不直观,对初学者不友好,所以本文以代码和其效果的形式来展示,保你一看就会。

2.2 配置SpringDoc

2.2.1 yml方式配置

springdoc:api-docs:# 是否开启接口文档enabled: trueswagger-ui:# 持久化认证数据persistAuthorization: trueinfo:# 标题title: '标题:${demo.name}多租户管理系统_接口文档'# 描述description: '描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...'# 版本version: '版本号: ${demo.version}'# 作者信息contact:name: Agazigiemail: 123456789@qq.comurl: www.baidu.comcomponents:# 鉴权方式配置security-schemes:apiKey:type: APIKEYin: HEADERname: ${sa-token.token-name}#这里定义了两个分组,可定义多个,也可以不定义group-configs:- group: 1.演示模块packages-to-scan: org.dromara.demo- group: 2.通用模块packages-to-scan: org.dromara.web- group: 3.系统模块packages-to-scan: org.dromara.system- group: 4.代码生成模块packages-to-scan: org.dromara.generator

在这里插入图片描述

2.2.2配置文档信息

得益于springboot的强大,我们只需添加一个依赖就可以使用API文档了,但是使用的都是默认值,我们当然也希望对其进行各种自定义的配置

  • 配置文档信息
    创建一个OpenAPI 的bean,配置文档名称等信息
@Configuration
public class SpringDocConfig {@Beanpublic OpenAPI myOpenAPI() {return new OpenAPI().info(new Info() //设置信息.title("程序员API") //标题.description("程序员的大本营") //描述信息.version("v1.0.0")  //版本.license(new License() //许可证.name("许可协议").url("https://shusheng007.top")).contact(new Contact() //作者信息.name("书生007").email("wangben850115@gmail.com"))).externalDocs(new ExternalDocumentation() //外部文档.description("ShuSheng007博客").url("https://shusheng007.top"));}// 指定扫描的包路径@Beanpublic GroupedOpenApi publicApi() {return GroupedOpenApi.builder().group("my_api") // 分组名称(自定义).packagesToScan("com.ls.ai.demo.controller") // 包路径.addOpenApiMethodFilter(methodPredicate()) // 方法级注解过滤.build();}// 定义过滤条件:仅包含带有 @Operation 注解的方法private Predicate<HandlerMethod> methodPredicate() {return handlerMethod ->handlerMethod.hasMethodAnnotation(io.swagger.v3.oas.annotations.Operation.class);}// 可选:过滤类级别的注解(如 @Tag)private Predicate<HandlerMethod> classPredicate() {return handlerMethod ->handlerMethod.getBeanType().isAnnotationPresent(io.swagger.v3.oas.annotations.tags.Tag.class);}}

效果:
在这里插入图片描述
里面的配置项很多,可以根据代码和截图对照一下,按照自己的需求配置即可

2.3配置文档分组

用来配置分组的,假如你有两类controller一类以/api为前缀,一类以/admin为前缀,就可以将其配置为两个分组。很多时候我们只有一个分组,所以就不需要下面的配置。

@Configuration
public class SpringDocConfig {...@Beanpublic GroupedOpenApi publicApi() {return GroupedOpenApi.builder().group("api").pathsToMatch("/api/**").build();}@Beanpublic GroupedOpenApi adminApi() {return GroupedOpenApi.builder().group("admin").pathsToMatch("/admin/**").build();}
}

效果:
在这里插入图片描述
可以通过右上角的下拉框选择要展示的group。

2.4使用注解

这个是咱们的重头戏,OpenApi规范提供了很多注解,下面是一些常用的

注解含义
@Tag用在controller类上,描述此controller的信息
@Operation用在controller的方法里,描述此api的信息
@Parameter用在controller方法里的参数上,描述参数信息
@Parameters用在controller方法里的参数上
@Schema用于Entity,以及Entity的属性上
@ApiResponse用在controller方法的返回值上
@ApiResponses用在controller方法的返回值上
@Hidden用在各种地方,用于隐藏其api

下面我们一起来看看效果

2.4.1 @Tag

@Tag(name = "程序员", description = "程序员乐园")
@RestController
@RequestMapping("/api/programmer")
public class ProgrammerController {
...
}

效果
在这里插入图片描述

2.4.2 @Operation

@Operation(summary = "创建程序员", description = "用于创建一个闷骚的程序员")
@PostMapping()
public Programmer createProgrammer(@RequestBody CreateProgrammerRequest request) {return new Programmer(666, "王二狗", request.getAge(), request.getProgrammingLang());
}

在这里插入图片描述
@Operation 其实很复杂的,我们可以将下面要用的@Parameter以及@ApiResponse都可以配置在它里面。

2.4.3 @Schema

@Schema 用于实体类和其属性,例如有如下代码:

@Schema(description = "创建程序员入参")
public class CreateProgrammerRequest {@Schema(description = "名称", example = "王二狗")private String name;@Schema(description = "年龄", example = "35")private Integer age;@Schema(description = "掌握的编程语言", type = "List", example = "[\"Java\",\"Sql\"]")private List<String> programmingLang;
}

效果:
在这里插入图片描述
还可以切换到 Schema选项进行查看
在这里插入图片描述

2.4.4 @NotNull

同时,Springdoc还支持 Java Bean Validation API 的注解,例如@NotNull

@Schema(description = "创建程序员入参")
public class CreateProgrammerRequest {@NotNull@Schema(description = "名称", example = "王二狗")private String name;@NotNull@Min(18)@Max(35)@Schema(description = "年龄", example = "35")private Integer age;...
}

效果:
在这里插入图片描述
注意红框中的内容,name和age右上角都有出现了一个红色的星星,表示是必填的。age也被限制了范围。

2.4.5 @Parameter

用于添加接口参数信息

@GetMapping("/{id}")
public Programmer getProgrammer(@Parameter(description = "程序员id") @PathVariable Integer id) {...
}

效果
在这里插入图片描述

2.4.6 @Parameters

@Parameter作用一样,但是可以批量添加,不用一个一个的写在参数前面

@Parameters(value = {@Parameter(name = "name", description = "姓名", in = ParameterIn.PATH),@Parameter(name = "age", description = "年龄", in = ParameterIn.QUERY)
})
@GetMapping("/{name}")
public List<Programmer> getProgrammers(@PathVariable("name") String name, @RequestParam("age") Integer age) { ...
}

parameters里的parameter使用name来找到方法中的入参,这块要对应上。

效果:

在这里插入图片描述

2.4.7 @ApiResponses 和@ApiResponse

顾名思义,此注解用来描述返回值的。

@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "成功",content = {@Content(mediaType = "application/json",schema = @Schema(implementation = Programmer.class))}),@ApiResponse(responseCode = "405", description = "非法输入",content = @Content)})@PostMapping()public Programmer createProgrammer(@RequestBody CreateProgrammerRequest request) {...}

效果
在这里插入图片描述
可见,我们成功配置了两种情况的返回值。但是我们一般不会手动给每个API 写上一堆@ApiResponse,那的多烦啊。业界通常会有一个统一的返回类型,例如

public class Result<T> implements Serializable {private int code;private String message;private T data;private String traceId;
}

还会有一个统一的异常处理类,使用@RestControllerAdvice标记,然后每个方法会捕捉对应的异常,只要我们使用@ResponseStatus来标记这些方法,springdoc就会自动生成相应的文档

@RestControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(value = Exception.class)@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)public Result handleException(HttpServletRequest httpServletRequest, Exception e) {return new Result(StatusCode.FAILED.getCode(), StatusCode.FAILED.getMessage(), null);}@ExceptionHandler(value = ApiException.class)@ResponseStatus(HttpStatus.BAD_REQUEST)public Result handleBusinessException(HttpServletRequest httpServletRequest, ApiException e) {return new Result(e.getCode(), e.getMessage(), null);}
}

例如我们有如下代码:

...
@RequestMapping(value = "/api/programmer",produces =  "application/json")
public class ProgrammerController {@GetMapping("/{id}")public Result<Programmer> getProgrammer(@Parameter(description = "程序员id") @PathVariable Integer id) {...}
}

生成的api文档如下:

在这里插入图片描述
可见,多了400和500。

四、认证授权

我们知道·swagger-ui·不仅可以看API文档也可以直接调用API,这点很牛逼。但有时我们的服务需要认证,否则就调用不通,那怎么办?稍安勿躁,OpenApi规范也考虑到了这个问题。

OpenAPI 3.0 支持下面的认证模式:

  • HTTP authentication schemes (使用Authorization header):
  • Basic
  • Bearer
  • Other HTTP schemes as defined by RFC 7235 and HTTP Authentication Scheme Registry
  • API keys in headers, query string or cookies
  • Cookie authentication
  • OAuth 2
  • OpenID Connect Discovery
    有的我也没用过,我们常用的就是Http Auth,以及OAuth2。本文以HTTP Bearer来作为我们服务的认证授权模式,如下图所示。
    在这里插入图片描述

3.1无需认证

当你的服务没有认证机制的话是可以直接调用的:

每个API 右上角都有一个try it out按钮,点击输入参数点击execute按钮即可,如下所示。
在这里插入图片描述

3.2需要认证

如果你的服务需要认证后才能调用,那么默认情况下就不行了。例如你使用了Spring Security,或者你自己写了个Filter 来实现认证功能。

下面是demo服务用来做认证的Filter,采用HTTP Bearer 模式。所以需要在请求的Authentication Header 里 携带token 123才能通过认证。

@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {...@Overrideprotected void doFilterInternal(HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull FilterChain filterChain) throws ServletException, IOException {...// get token from header [Authorization: Bearer <token>]String authHeader = request.getHeader(AUTH_HEADER);...String authToken = authHeader.split(" ")[1];if (!"123".equals(authToken)) {genUnauthResponse(response);return;}filterChain.doFilter(request, response);}

所以当从swagger-ui上调用API时,返回了401,如下图所示。那怎么才能正常调用API呢?接下来让我们看一下
在这里插入图片描述

Springdoc 使用@SecurityScheme 来定义一个安全模式,我们可以定义全局的,也可以针对某个controller定义类级别的,我们这里定义一个全局的。

  1. 在Application类上添加@SecurityScheme
@SecurityScheme(name = "api_token", type = SecuritySchemeType.HTTP, scheme ="bearer", in = SecuritySchemeIn.HEADER)
@SpringBootApplication
public class SpringdocIntegrateApplication {public static void main(String[] args) {SpringApplication.run(SpringdocIntegrateApplication.class, args);}
}

我们定义了一个名为api_token的安全模式,并指定了其使用HTTP Bearer的方式。

  1. 使此安全模式生效
@Configuration
public class SpringDocConfig {@Beanpublic OpenAPI myOpenAPI() {return new OpenAPI()....security(List.of(new SecurityRequirement().addList("api_token")));}
}

注意api_token正是我们第一步定义的那个Security schema。

经过上面两步后查看生成的API文档,你会发现其右上角出现了一个Authorize的按钮, 而且每个API的右边也出现了一把小锁,如下图所示。
在这里插入图片描述
当点击Authorize按钮后会弹出一个让你提供认证信息的弹出,其根据不同的认证方式会有所区别。

在这里插入图片描述
我们这里需要输入一个token,然后点击Authorize按钮后关闭此弹窗。你会发现每个API右边的那把小锁从打开状态变为了关闭状态,颜色也从灰色变成了黑色,证明其生效了。

然后我们再来请求一下API,就会正常返回了。

  1. 声明是否需要认证
    默认情况下按照上面两步设置后,整个应用程序的API生效,但是有的API是不需要认证的,例如登录。 对于这种情况,我们可以使用@SecurityRequirements()来设置。
@RestController
@RequestMapping(value = "/admin",produces =  "application/json")
public class AuthController {...@SecurityRequirements()@PostMapping("/login")public Result<String> login(@RequestBody LoginRequest request){return Result.ok("123");}
}

@SecurityRequirements() 里面需要一个String数组,里面列出需要使用的@SecurityScheme,例如我们这里的api_token。如果不写就说明不需要任何的安全模式,这里就是这种情况。
在这里插入图片描述
从上图可以看出,/admin/login这个API右边没有小锁的标志,表示其不需要认证。针对本案例来说,不需要认证的意思就是:在发起这个请求的时候,不在Authentication header里面附加token。

这里只是展示了HTTP bearer 这种安全模式,其他的原理类似,小朋友们可以开动你们聪明的大脑研究一下,给补充到这里。

五、SpringDoc整合Knife4j

1.前世今生

在更名为Knife4j之前,原来的名称是叫swagger-bootstrap-ui,这是两种不一样风格的Ui,对比情况如下:

名称开发语言&框架状态最后版本风格
Knife4jJava、JavaScript、Vue持续更新中…黑色
swagger-bootstrap-uiJava、JavaScript、jQuery停更1.9.6蓝色

Knife4j从开源至今,目前主要经历版本的变化,分别如下:

版本说明
1.0~1.9.6名称是叫swagger-bootstrap-ui,蓝色风格Ui
1.9.6蓝色皮肤风格,开始更名,增加更多后端模块
2.0~2.0.5Ui基于Vue2.0+AntdV重写,黑色风格,参考示例,底层依赖的springfox框架版本是2.9.2,仅提供Swagger2规范的适配
2.0.6~2.0.9底层springfox框架版本升级至2.10.5,仅提供Swagger2规范的适配
3.0~3.0.3底层依赖springfox框架版本升级至3.0.3,OpenAPI规范是v3,过度版本,建议开发者不要使用
4.0~区分OpenAPI2和Swagger3的Maven坐标artifactId
OpenAPI2规范服务端解析框架稳定在springfox2.10.5
OpenAPI3框架服务端解析跟随springdoc项目更新迭代
建议开发者使用该版本,请参考4.x升级文档

2.Spring Boot版本兼容性

首先,确保您了解您的项目所使用的Spring Boot版本。
以下是一些常见的Spring Boot版本及其对应的Knife4j版本兼容推荐:

Spring Boot版本Knife4j Swagger2规范Knife4j OpenAPI3规范
1.5.x~2.0.0<Knife4j 2.0.0>=Knife4j 4.0.0
2.0~2.2Knife4j 2.0.0 ~ 2.0.6>=Knife4j 4.0.0
2.2.x~2.4.0Knife4j 2.0.6 ~ 2.0.9>=Knife4j 4.0.0
2.4.0~2.7.x>=Knife4j 4.0.0>=Knife4j 4.0.0
>= 3.0>=Knife4j 4.0.0>=Knife4j 4.0.0

Knife4j在之前的版本更新中,逐渐提供了一些服务端适配的增强特性功能。

但是开发者应该明白,不管是Swagger2规范还是OpenAPI3规范,Knife4j的最新版本的纯Ui版本,是可以适配Spring Boot所有版本的。

如果你不考虑使用Knife4j提供的服务端增强功能,引入Knife4j的纯Ui版本没有任何限制。只需要考虑不同的规范即可

3.Spring Boot 3

提示
Spring Boot 3 只支持OpenAPI3规范
Knife4j提供的starter已经引用springdoc-openapi的jar,开发者需注意避免jar包冲突
JDK版本必须 >= 17
详细Demo请参考knife4j-spring-boot3-demo

首先,引用Knife4j的starter,Maven坐标如下:

<dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId><version>4.5.0</version>
</dependency>

其实现在就可以使用Knife4j了,暂不做其他配置,启动项目,浏览器输入http://localhost:8080/doc.html查看接口文档
由于我们没有进行任何的属性配置,所以看到的页面是knife4j的初始页面
在这里插入图片描述

引入之后,其余的配置,开发者即可完全参考springdoc-openapi的项目说明,Knife4j只提供了增强部分,如果要启用Knife4j的增强功能,可以在配置文件中进行开启

部分配置如下:

# springdoc-openapi项目配置
springdoc:swagger-ui:path: /swagger-ui.htmltags-sorter: alphaoperations-sorter: alphaapi-docs:path: /v3/api-docsgroup-configs:- group: 'default'paths-to-match: '/**'packages-to-scan: com.xiaominfo.knife4j.demo.web
# knife4j的增强配置,不需要增强可以不配
knife4j:enable: truesetting:language: zh_cn

Knife4j更多增强配置明细,请移步文档进行查看

最后,使用OpenAPI3的规范注解,注释各个Spring的REST接口,示例代码如下:

@RestController
@RequestMapping("body")
@Tag(name = "body参数")
public class BodyController {@Operation(summary = "普通body请求")@PostMapping("/body")public ResponseEntity<FileResp> body(@RequestBody FileResp fileResp){return ResponseEntity.ok(fileResp);}@Operation(summary = "普通body请求+Param+Header+Path")@Parameters({@Parameter(name = "id",description = "文件id",in = ParameterIn.PATH),@Parameter(name = "token",description = "请求token",required = true,in = ParameterIn.HEADER),@Parameter(name = "name",description = "文件名称",required = true,in=ParameterIn.QUERY)})@PostMapping("/bodyParamHeaderPath/{id}")public ResponseEntity<FileResp> bodyParamHeaderPath(@PathVariable("id") String id,@RequestHeader("token") String token, @RequestParam("name")String name,@RequestBody FileResp fileResp){fileResp.setName(fileResp.getName()+",receiveName:"+name+",token:"+token+",pathID:"+id);return ResponseEntity.ok(fileResp);}
}

最后,访问Knife4j的文档地址:http://ip:port/doc.html即可查看文档

4.Spring Boot 2

提示
Spring Boot 版本建议 2.4.0~3.0.0之间
Spring Boot 版本 < 2.4 版本则建议选择Knife4j 4.0之前的版本
Spring Boot 2 + OpenAPI2 demo:knife4j-spring-boot27-demo
Spring Boot 2 + OpenAPI3 demo:knife4j-springdoc-openapi-demo

OpenAPI2
OpenAPI2(Swagger)规范是Knife4j之前一直提供支持的版本,底层依赖框架为Springfox,此次在4.0版本开始

Knife4j有了新的变化,主要有以下几点:

  • Springfox版本选择的依然是2.10.5版本,而并非springfox最新3.0.0版本
  • 不支持以Springfox框架为基础的OpenAPI3规范,放弃Springfox项目的后续版本适配支持工作
  • Spring Boot 版本建议 2.4.0~3.0.0之间
    可以使用 knife4j-openapi2-spring-boot-starter,maven 坐标如下:
<dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-openapi2-spring-boot-starter</artifactId><version>4.5.0</version>
</dependency>

配置yml属性,如下:

knife4j:enable: trueopenapi:title: Knife4j官方文档description: "`我是测试`,**你知道吗**# aaa"email: xiaoymin@foxmail.comconcat: 八一菜刀url: https://docs.xiaominfo.comversion: v4.0license: Apache 2.0license-url: https://stackoverflow.com/terms-of-service-url: https://stackoverflow.com/group:test1:group-name: 分组名称api-rule: packageapi-rule-resources:- com.knife4j.demo.new3

最后,访问Knife4j的文档地址:http://ip:port/doc.html即可查看文档
OpenAPI3
OpenAPI3的规范,目前针对Java的Spring Boot项目,主要支持的有2个版本

  • springfox 3.0.0: 同时兼容OpenAPI2以及OpenAPI3,但是停更很久了
  • springdoc-openapi: 兼容OpenAPI3规范,更新速度频繁
    Knife4j在只有的OpenAPI3规范中,底层基础框架选择springdoc-openapi项目

针对Springfox3.0.0版本会放弃。

建议开发者如果使用OpenAPI3规范的话,也尽快迁移过来。

可以使用 knife4j-openapi3-spring-boot-starter,maven 坐标如下:

<dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-openapi3-spring-boot-starter</artifactId><version>4.5.0</version>
</dependency>

引入jar包后,同上面的Spring Boot 3版本使用方式一样,进行配置即可。


http://www.mrgr.cn/news/97631.html

相关文章:

  • Dev C++下载及安装
  • fpga系列 HDL:跨时钟域同步 4-phase handshake(四相握手通信协议,请求-确认机制)浅释与代码实现
  • 嵌入式---加速度计
  • 搭建hadoop集群模式并运行
  • SearXNG
  • MCP基础学习一:MCP概述与基础
  • Linux 性能调优之CPU调优认知
  • 【回眸】Linux 内核 (十三)进程间通讯 之 共享内存
  • QML Loader:动态加载与控件创建
  • MCP-Playwright: 赋予AI模型操控浏览器的能力
  • c# 数据结构 链表篇 有关单链表的一切
  • 力扣hot100_回溯(2)_python版本
  • Wideband Sparse Reconstruction for Scanning Radar论文阅读
  • 【Pandas】pandas DataFrame infer_objects
  • AnimateCC基础教学:随机抽取花名册,不能重复
  • nginx如何实现负载均衡?
  • Python 快速搭建一个小型的小行星轨道预测模型 Demo
  • 数字电子技术基础(四十)——使用Digital软件和Multisim软件模拟显示译码器
  • C++隐式转换的机制、风险与消除方法
  • Model Context Protocol(MCP)介绍