自定义实现 Java17+SpringBoot3+OpenAPI+Knife4j Starter

文章目录

    • 前言
    • 正文
      • 1 创建starter项目
        • 1.1 依赖文件
        • 1.2 配置信息
      • 2 自定义starer代码开发
        • 2.1 配置字段的定义
        • 2.2 自动配置类
      • 3 验证starter
        • 3.1 测试项目的配置
        • 3.2 功能配置 application.yml
        • 3.3 测试代码
          • 3.3.1 实体类
          • 3.3.2 控制器1
          • 3.3.2 控制器2
      • 4 效果展示
        • 4.1 主页
        • 4.2 实体类列表
        • 4.3 自定义文档
        • 4.4 接口文档
    • 附录
      • 1 参考文档
      • 2 注意事项
        • 2.1 测试项目git地址
        • 2.2 starter项目目录

前言

一直以来,在接口文档这块没怎么尝试过比较新的技术点,使用的都是swagger2 和 低版本的 knife4j

本次就研究下在高版本的情况下,基于swagger的接口文档有什么变化。

本文基于以下环境:

组件版本
Java17
SpringBoot3.2.5
knife4j-openapi3-jakarta-spring-boot-starter4.5.0

正文

1 创建starter项目

创建一个Maven类型的SpringBoot项目,作为starter组件的开发。

1.1 依赖文件

Maven项目中,使用pom.xml文件来管理依赖。这里列举以下在starter项目中,使用到的依赖坐标。
此处指定了Java版本、Maven编译Java代码时使用的Java版本,并切设置项目的编码格式为UTF-8格式。

<properties><java.version>17</java.version><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><spring-boot.version>3.2.5</spring-boot.version><knife4j-openapi3-jakarta-version>4.5.0</knife4j-openapi3-jakarta-version>
</properties>
<dependencies>
<!-- https://www.mvncenter.com/search/org.springframework.boot/spring-boot-starter-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId>
</dependency><!-- https://www.mvncenter.com/search/com.github.xiaoymin/knife4j-openapi3-jakarta-spring-boot-starter-->
<dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId><version>${knife4j-openapi3-jakarta-version}</version>
</dependency><dependency><groupId>jakarta.servlet</groupId><artifactId>jakarta.servlet-api</artifactId>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId>
</dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional><scope>compile</scope>
</dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional>
</dependency>
</dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring-boot.version}</version><type>pom</type><scope>import</scope></dependency></dependencies>
</dependencyManagement>
1.2 配置信息

因为这个starter组件是基于SpringBoot3.x开发的,所以在自定义starter时,使用新的写法,不在直接使用spring.factories文件对自动配置类进行配置。

resources目录下,创建目录 META-INF/spring,然后创建名为org.springframework.boot.autoconfigure.AutoConfiguration.imports的文件备用。

2 自定义starer代码开发

自定义starter的开发,代码量不多,主要是一个自动配置类和配置字段的定义。

2.1 配置字段的定义
package org.feng.basic.swagger.properties;import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;/*** Swagger 配置属性** @author feng*/
@Data
@ConfigurationProperties(prefix = SwaggerProperties.PREFIX)
public class SwaggerProperties {public static final String PREFIX = "swagger.config";private String title;private String version;private String description;private String termsOfService;private String licenseName;private String licenseUrl;private Contact contact;@Datapublic static class Contact {private String name;private String url;private String email;}
}
2.2 自动配置类

使用注解 ConditionalOnProperty 来指定是否进行配置bean的加载。

主要条件是 knife4j.enable=true,当满足条件时,配置类中的内容生效。

除此之外,在文件org.springframework.boot.autoconfigure.AutoConfiguration.imports 中,添加自动配置类的全名(包名+类名)。
比如,我项目中的自动配置类会配置为:org.feng.basic.swagger.SwaggerAutoConfiguration

package org.feng.basic.swagger;import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;
import org.feng.basic.swagger.properties.SwaggerProperties;
import org.springdoc.core.customizers.GlobalOpenApiCustomizer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ThreadLocalRandom;/*** swagger自动配置类** @author feng*/
@ConditionalOnProperty(prefix = "knife4j", name = "enable", havingValue = SwaggerAutoConfiguration.TRUE, matchIfMissing = true)
@EnableConfigurationProperties(SwaggerProperties.class)
public class SwaggerAutoConfiguration {public static final String TRUE = "true";private final SwaggerProperties swaggerProperties;public SwaggerAutoConfiguration(@Autowired SwaggerProperties swaggerProperties) {this.swaggerProperties = swaggerProperties;}/*** 根据@Tag 上的排序,写入x-order** @return the global open api customizer*/@Beanpublic GlobalOpenApiCustomizer orderGlobalOpenApiCustomizer() {return openApi -> {
//            if (openApi.getTags() != null) {
//                openApi.getTags().forEach(tag -> {
//                    Map<String, Object> map = new HashMap<>();
//                    map.put("x-order", ThreadLocalRandom.current().nextInt(0, 100));
//                    tag.setExtensions(map);
//                });
//            }
//            if (openApi.getPaths() != null) {
//                openApi.addExtension("x-test123", "333");
//                openApi.getPaths().addExtension("x-abb", ThreadLocalRandom.current().nextInt(0, 100));
//            }};}@Beanpublic OpenAPI openApi() {return new OpenAPI().info(new Info().title(swaggerProperties.getTitle()).description(swaggerProperties.getDescription()).version(swaggerProperties.getVersion()).termsOfService(swaggerProperties.getTermsOfService()).contact(new Contact().name(swaggerProperties.getContact().getName()).url(swaggerProperties.getContact().getUrl()).email(swaggerProperties.getContact().getEmail())).license(new License().name(swaggerProperties.getLicenseName()).url(swaggerProperties.getLicenseUrl())));}
}

3 验证starter

在本地开发时,将 starter 项目进行 mvn install打包并上传jar到本地的maven仓库中。

随后,创建一个新项目,在新项目中使用刚刚写好的starter组件。起始就是引入它的maven坐标,这里的版本选择和starter保持一致。

3.1 测试项目的配置

在测试项目中,使用spring-boot-web组件。具体的pom.xml配置如下:

    <properties><java.version>17</java.version><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><spring-boot.version>3.2.5</spring-boot.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.feng</groupId><artifactId>spring-boot-swagger-starter</artifactId><version>0.0.1-SNAPSHOT</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring-boot.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.1</version><configuration><source>17</source><target>17</target><encoding>UTF-8</encoding></configuration></plugin><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>${spring-boot.version}</version><configuration><mainClass>org.feng.SpringBootSwaggerStarterTestApplication</mainClass><skip>true</skip></configuration><executions><execution><id>repackage</id><goals><goal>repackage</goal></goals></execution></executions></plugin></plugins></build>
3.2 功能配置 application.yml
server:port: 17813servlet:context-path: /
spring:servlet:multipart:max-file-size: 100MBmax-request-size: 100MBapplication:name: knife4j-spring-boot3-demo
springdoc:swagger-ui:path: /swagger-ui.htmltags-sorter: alpha#operations-sorter: orderapi-docs:path: /v3/api-docs# 分组配置,对不同的包进行分组group-configs:- group: 'test1'display-name: '测试1'paths-to-match: '/**'packages-to-scan: org.feng.test1.controller- group: 'test2'display-name: '测试2'paths-to-match: '/**'packages-to-scan: org.feng.test2.controllerdefault-flat-param-object: trueknife4j:enable: truesetting:language: zh_cnswagger-model-name: 实体类列表# 是否在每个Debug调试栏后显示刷新变量按钮,默认不显示enableReloadCacheParameter: true# 是否开启界面中对某接口的版本控制,如果开启,后端变化后Ui界面会存在小蓝点enableVersion: true# 针对RequestMapping的接口请求类型,在不指定参数类型的情况下,如果不过滤,默认会显示7个类型的接口地址参数,如果开启此配置,默认展示一个Post类型的接口地址enableFilterMultipartApis: true# 是否开启动态参数调试功能enableDynamicParameter: true# 是否显示FooterenableFooter: falseenableFooterCustom: truefooterCustomContent: Apache License xx | Copyright  xxxx [xxxx-xxxx](https://xxxx.com/xxxx)# 自定义主页enable-home-custom: falsehome-custom-path: classpath:markdown/README.mddocuments:- name: 自定义文档1locations: classpath:markdown/*group: 测试1- name: 自定义文档2locations: classpath:markdown1/*group: 测试2# 启用简单的权限管理,访问接口文档需要登录basic:enable: trueusername: abcpassword: 123# http://www.knife4j.net/insight:enable: falseservice-name: boot3secret: S6CsnS8AnPVyb8vvChcdXm4R3p6A6KlAISxBg3IIEgk=server: http://localhost:10086namespace: spring3swagger:config:title: '${spring.application.name}的在线文档'description: '在线文档'version: 'v1'terms-of-service: ''contact:name: 'feng'email: 'fengsoshuai@163.com'url: ''license-name: ''license-url: ''
3.3 测试代码
3.3.1 实体类
package org.feng.model;import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;import java.io.Serial;
import java.io.Serializable;
import java.time.LocalDateTime;/*** TODO** @author feng*/
@Data
public class User implements Serializable {@Serialprivate static final long serialVersionUID = 7250829747040287299L;@Schema(title = "userId", description = "用户ID", defaultValue = "1")private Long id;@Schema(description = "名称", requiredMode = Schema.RequiredMode.REQUIRED)private String name;@Schema(description = "注册日期")private LocalDateTime registerDate;
}
3.3.2 控制器1
package org.feng.test1.controller;import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.feng.model.User;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;/*** TODO** @author feng*/
@Tag(name = "test1")
@RestController
@RequestMapping("/test1")
public class Test1Controller {@Operation(summary = "获取详情")@GetMapping("/getOneById")public User getOneDetailById(@RequestParam(required = false) Integer id) {return null;}
}
3.3.2 控制器2
package org.feng.test2.controller;import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.feng.model.User;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;/*** TODO** @author feng*/
@Tag(name = "test2")
@RestController
@RequestMapping("/test2")
public class Test2Controller {@Operation(summary = "获取详情")@GetMapping("/getOneById")public User getOneDetailById(@RequestParam(required = false) Integer id) {return null;}
}

4 效果展示

启动项目后,访问链接:http://localhost:17813/doc.html#/home

4.1 主页

在这里插入图片描述

4.2 实体类列表

在这里插入图片描述

4.3 自定义文档

在这里插入图片描述

4.4 接口文档

在这里插入图片描述

附录

1 参考文档

  • https://doc.xiaominfo.com/docs/quick-start#spring-boot-3
  • https://www.mvncenter.com/search/com.github.xiaoymin/knife4j-openapi3-jakarta-spring-boot-starter
  • https://www.mvncenter.com/search/org.springframework.boot/spring-boot-starter
  • https://gitee.com/xiaoym/swagger-bootstrap-ui-demo/tree/master/knife4j-spring-boot3-demo
  • http://www.knife4j.net/

2 注意事项

2.1 测试项目git地址

https://gitee.com/fengsoshuai/spring-boot-swagger-starter-test

注意:因为starter的代码和配置很少,就不单独建立仓库了。仓库只包含测试项目的代码。starter的代码和配置可以从本文中粘贴出来。

2.2 starter项目目录

在这里插入图片描述

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

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

相关文章

Windows快速部署DCNv4(成功版)

文章目录 一、介绍二、编译DCNv42.1 下载源码2.2 编译DCNv4 三、报错提示3.1 Cuda is not available3.2 需要Microsoft Visual C 14.0 一、介绍 论文链接&#xff1a;[https://arxiv.org/pdf/2401.06197.pdf] (https://arxiv.org/pdf/2401.06197.pdf)   在这篇文章中介绍了一…

【Android】Kotlin学习之Kotlin方法的声明和传参

方法声明 普通类的方法 静态类的方法 不需要构建实例对象, 可以通过类名直接访问静态方法 : NumUtil.double(1) companion object 伴生类的方法 使用companion object 在普通类里定义静态方法 参数 括号内传入方法 : 当参数是方法时, 并且是最后一个参数 , 可以使用括号外…

Linux -- > vim

vi和vim是什么 vi和vim是两款流行的文本编辑器&#xff0c;广泛用于Unix和类Unix系统中。它们以其强大的功能和灵活的编辑能力而闻名&#xff0c;特别是在编程和系统管理中非常受欢迎。 vi&#xff08;Visual Interface&#xff09; vi是最初的文本编辑器之一&#xff0c;由…

外观模式详解

外观模式 1 概述 有些人可能炒过股票&#xff0c;但其实大部分人都不太懂&#xff0c;这种没有足够了解证券知识的情况下做股票是很容易亏钱的&#xff0c;刚开始炒股肯定都会想&#xff0c;如果有个懂行的帮帮手就好&#xff0c;其实基金就是个好帮手&#xff0c;支付宝里就…

【智能算法】最优捕食算法(OFA)原理及实现

目录 1.背景2.算法原理2.1算法思想2.2算法过程 3.结果展示4.参考文献5.代码获取 1.背景 2017年&#xff0c;GY Zhu受到动物行为生态学理论启发&#xff0c;提出了最优捕食算法&#xff08;Optimal Foraging Algorithm, OFA&#xff09;。 2.算法原理 2.1算法思想 OFA灵感来源…

Lab4: traps

RISC-V assembly Which registers contain arguments to functions? For example, which register holds 13 in mains call to printf? 根据RISC-V函数调用规范&#xff0c;函数的前8个参数使用a0-a7寄存器传递。 当main函数调用printf函数时&#xff0c;a2寄存器保存13 …

MVCC 详解

介绍 MVCC&#xff0c;全称 Multi-Version Concurrency Control&#xff0c;即多版本并发控制 MVCC的目的主要是为了提高数据库并发性能&#xff0c;用更好的方式去处理读-写冲突&#xff0c;做到即使有读写冲突时&#xff0c;也能做到不加锁。 这里的多版本指的是数据库中同时…

Springboot+Vue项目-基于Java+MySQL的毕业就业信息管理系统(附源码+演示视频+LW)

大家好&#xff01;我是程序猿老A&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;Java毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计 &…

java异常,日志,线程堆栈与Jvm调优

一.知识目录&#xff1a; 二.什么是java异常&#xff1a; 2.1 Throwable类中的重要方法: (1)四个构造方法&#xff08;用来构造throwable对象&#xff0c;不同构造方法可以传递不同的参数值&#xff09;&#xff1a; /** 构造一个将 null 作为其详细消息的新 throwable */ Thr…

在STM32中用寄存器方式点亮流水灯

文章目录 实验资料一、对寄存器的理解1.通俗认识寄存器2.深入了解寄存器&#xff08;1&#xff09;端口配置低寄存器&#xff08;配置0到7引脚的寄存器&#xff09;&#xff08;2&#xff09;端口配置高寄存器&#xff08;配置8到15引脚&#xff09; 3.GPIO口的功能描述 二、配…

鸿蒙内核源码分析(Shell解析篇) | 应用窥视内核的窗口

系列篇从内核视角用一句话概括shell的底层实现为&#xff1a;两个任务&#xff0c;三个阶段。其本质是独立进程&#xff0c;因而划到进程管理模块。每次创建shell进程都会再创建两个任务。 客户端任务(ShellEntry)&#xff1a; 负责接受来自终端(控制台)敲入的一个个字符&…

【云原生】 Kubernetes核心概念

目录 引言 一、部署方式回溯 &#xff08;一&#xff09;传统部署时代 &#xff08;二&#xff09;虚拟化部署时代 &#xff08;三&#xff09;容器部署时代 二、Kubernetes基本介绍 &#xff08;一&#xff09;为什么使用k8s &#xff08;二&#xff09;主要功能 &am…

乡村振兴与数字乡村建设:加强农村信息化建设,推动数字乡村发展,提升乡村治理和服务水平,构建智慧化的美丽乡村

目录 一、引言 二、数字乡村建设的必要性 1、推动农村经济转型升级 2、提升乡村治理水平 3、改善乡村民生福祉 三、数字乡村建设的现状与挑战 1、现状 2、挑战 四、数字乡村建设的未来发展路径 1、加强农村信息化基础设施建设 2、提升农民信息素养和技能水平 3、制…

py黑帽子学习笔记_环境准备

1 下载os装os 下载一个kali虚机镜像然后用虚机管理软件创虚机&#xff0c;装完如下图&#xff0c;我用的版本是2024.1的版本kali-linux-2024.1-installer-amd64&#xff0c;可以从镜像站下载&#xff0c;官网下的慢还断网Index of /kali-images/kali-2024.1/ | 清华大学开源软…

C++高精度算法-加法

引子 在C++的运算中,难免会出现很大很大的数,下面是各个关键字的表示范围 但是如果要表示的数超过了long long可以表示的最大值( 2 64 2^{64} 264-1) 怎么办呢? 如果强制表示,就会溢出,这里的溢出大家可以自行百度,反正就是会出一些-5665434之类的数 现在,就要切入正…

网络基础-Telnet协议

Telnet&#xff08;Telecommunication Network&#xff09;是一种基于文本的远程终端协议&#xff0c;允许用户通过网络连接到远程计算机&#xff0c;并在远程计算机上执行命令&#xff1b;它使用TCP作为传输层协议&#xff0c;并依赖于网络连接在客户端和服务器之间进行通信&a…

MySQL 身份认证漏洞 CVE-2012-2122

漏洞影响版本 MariaDB versions from 5.1.62, 5.2.12, 5.3.6, 5.5.23 are not.MySQL versions from 5.1.63, 5.5.24, 5.6.6 are not.演示 开启靶场 进入漏洞目录 cd /root/vulhub/mysql/CVE-2012-2122开启漏洞靶场 docker-compose up -d攻击 直接 运行 这个命令 for i i…

分布式与一致性协议之PBFT算法(二)

PBFT算法 如何替换作恶的主节点 虽然PBFT算法可以防止备份节点作恶&#xff0c;因为这个算法是由主节点和备份节点组成的&#xff0c;但是&#xff0c;如果主节点作恶(比如主机点接收到了客户端的请求&#xff0c;但就是默不作声&#xff0c;不执行三阶段协议)&#xff0c;那…

Line Buffer概述

buffer在芯片物理上一般指的是SRAM&#xff0c;也可以指寄存器组。buffer的作用是用来在逻辑芯片上暂时存储数据&#xff0c;但不会是大量的数据。如果是大量数据一般会使用DRAM&#xff08;典型的指DDR&#xff09;作为存储芯片&#xff0c;用来存储大密度数据。line buffer可…

优化资源利用,用C++内存池点亮编程之路

内存池介绍(Memory Pool): 它是一种内存分配方式&#xff0c;通过预先分配和复用内存块。 在真正使用内存之前&#xff0c;先申请一大块内存备用。当有新的内存需求时&#xff0c;就从内存池中分出一部分内存块&#xff0c; 若内存块不够再继续申请新的内存。如果我们不需要…