☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 免费无限量使用 DeepSeek R1 模型☜☜☜

随着 java 生态向 jdk 17 及 jakarta ee 的演进,许多项目面临从 jdk 8 升级的挑战,其中 swagger(api 文档工具)的兼容性调整尤为关键。本文将从 技术栈差异、升级迁移步骤、常见问题 等多个维度,解析 jdk 8(springfox)向 jdk 17(springdoc/knife4j)的升级路径。
Records 等新特性,但移除了部分旧 API(如 javax.servlet)。直接影响:基于 JDK 8 构建的 SpringFox(Swagger 2.x)因依赖旧规范无法兼容新版本。Jakarta EE 的崛起:Java EE 移交 Eclipse 基金会后更名为 Jakarta EE,包名从 javax.* 改为 jakarta.*。核心冲突:Spring Boot 3.x 和 SpringDoc(Swagger 3.x)强制依赖 Jakarta EE 9+,导致旧项目升级时需全局替换包名。SpringFox 已停止维护,存在未修复漏洞(如 CVE-2021-28170)。功能需求:SpringDoc 支持 OpenAPI 3.0 规范,提供更灵活的文档定义和响应示例。生态兼容:微服务、云原生场景下,JDK 17 的容器化支持更优。特性 |
JDK 8 + SpringFox (Swagger 2.x) |
JDK 17 + SpringDoc/Knife4j (Swagger 3.x) |
|---|---|---|
核心框架 |
SpringFox 2.x(已停止维护) |
SpringDoc OpenAPI 3.x(官方推荐) |
JDK 兼容性 |
仅支持 JDK 8~11 |
支持 JDK 17+ 的模块化特性 |
Spring Boot 支持 |
Spring Boot 2.x |
Spring Boot 3.x(兼容 2.7.x) |
Servlet 规范 |
基于 javax.servlet |
迁移至 jakarta.servlet(Jakarta EE 9+) |
注解库 |
io.swagger.annotations |
io.swagger.v3.oas.annotations |
注解风格 |
@Api, @ApiOperation |
@Tag, @Operation(更符合 OpenAPI 3.0) |
依赖管理 |
需手动管理版本,易冲突 |
通过 Spring Boot Starter 简化依赖 |
文档生成 |
需配置 Docket |
自动扫描,通过 OpenAPI Bean 全局配置 |
文档规范 |
OpenAPI 2.0 |
OpenAPI 3.0 |
UI 工具 |
Swagger UI(基础功能) |
Knife4j(增强功能,支持离线文档、权限控制、接口分组等) |
维护状态 |
停止维护(最后版本 3.0.0) |
活跃维护(SpringDoc 2.x + Knife4j 4.x) |
技术栈 |
JDK 8 |
JDK 11 |
JDK 17 |
Spring Boot 2.7.x |
Spring Boot 3.x |
|---|---|---|---|---|---|
SpringFox 2.x |
✅ |
⚠️ 部分兼容 |
❌ |
✅ |
❌ |
SpringDoc 1.x |
✅ |
✅ |
❌ |
✅ |
❌ |
SpringDoc 2.x |
❌ |
✅ |
✅ |
✅ |
✅ |
控制器注解迁移:
SpringFox (Swagger 2.x) |
SpringDoc (OpenAPI 3.x) |
用途 |
示例 |
|---|---|---|---|
@Api |
@Tag |
标记控制器类的作用 |
@Tag(name = "用户管理", description = "用户接口") |
@ApiOperation |
@Operation |
描述接口方法的功能 |
@Operation(summary = "创建用户", description = "根据DTO创建用户") |
@ApiParam |
@Parameter |
描述接口参数(路径、查询、表单参数等) |
@Parameter(name = "id", description = "用户ID", required = true) |
@ApiResponse |
@ApiResponse |
定义接口的响应状态码和描述 |
@ApiResponse(responseCode = "404", description = "用户不存在") |
@ApiIgnore |
@Hidden 或 @Parameter(hidden = true) |
隐藏接口或参数 |
@Hidden // 隐藏整个接口方法 |
@ApiImplicitParams |
@Parameters + @Parameter |
描述非直接声明的参数(如 Header 参数) |
@Parameters({ @Parameter(name = "token", in = HEADER, description = "认证令牌") }) |
@ApiImplicitParam |
@Parameter |
单个隐式参数定义 |
同上 |
<code class="javascript">// JDK 8(SpringFox)@Api(tags = "用户管理", description = "用户接口")@RestControllerpublic class UserController { @ApiOperation("创建用户") @PostMapping("/users") public User createUser(@ApiParam("用户DTO") @RequestBody UserDTO dto) { // ... } @ApiImplicitParams({ @ApiImplicitParam(name = "token", value = "认证令牌", paramType = "header") }) @GetMapping("/profile") public UserProfile getProfile() { //... }}</code><code class="javascript">// JDK 17(SpringDoc)@Tag(name = "用户管理", description = "用户接口")@RestControllerpublic class UserController { @Operation(summary = "创建用户", description = "根据DTO创建用户") @PostMapping("/users") public User createUser(@Parameter(description = "用户DTO", required = true) @RequestBody UserDTO dto) { // ... } @Parameters({ @Parameter(name = "token", description = "认证令牌", in = ParameterIn.HEADER) }) @GetMapping("/profile") public UserProfile getProfile() { // ... }}</code>模型类注解迁移:
SpringFox (Swagger 2.x) |
SpringDoc (OpenAPI 3.x) |
用途 |
示例 |
|---|---|---|---|
@ApiModel |
@Schema |
描述数据模型类 |
@Schema(name = "UserDTO", description = "用户传输对象") |
@ApiModelProperty |
@Schema |
描述模型字段的详细信息 |
@Schema(description = "用户名", example = "张三", requiredMode = REQUIRED) |
<code class="javascript">// JDK 8(SpringFox)@ApiModel(value = "User", description = "用户实体")public class User { @ApiModelProperty(value = "用户名", required = true, example = "张三") private String name;}</code><code class="javascript">// JDK 17(SpringDoc)@Schema(name = "User", description = "用户实体")public class User { @Schema(description = "用户名", example = "张三", requiredMode = Schema.RequiredMode.REQUIRED) private String name;}</code>是否还需要传统 SwaggerConfig?
不需要:Knife4j OpenAPI3 基于 SpringDoc,无需配置 Docket 或 Swagger2Markup。必要配置:仅需定义 OpenAPI Bean(如上文的 OpenApiConfig)即可。<code class="javascript">@Configurationpublic class OpenApiConfig { @Bean public OpenAPI customOpenAPI() { return new OpenAPI() .info(new Info() .title("API 文档") .version("1.0") .description("JDK 17 迁移示例") .contact(new Contact().name("xcbeyond技术支持").email("support@example.com")) .license(new License().name("Apache 2.0").url("https://springdoc.org"))) .externalDocs(new ExternalDocumentation() .description("详细文档") .url("https://xcbeyond.com")) .components(new Components() .addSecuritySchemes("BearerAuth", new SecurityScheme() .type(SecurityScheme.Type.HTTP) .scheme("bearer") .bearerFormat("JWT"))); }}</code>在微服务架构中,API 文档分组配置的核心管理需求是:
模块化展示:将不同业务域(用户/订单/支付)分离展示。权限隔离:区分公共 API 和管理 API。版本管理:同时维护 v1 和 v2 接口。依赖解耦:避免单个文档过大导致加载性能问题。分组配置参数详解:
配置方法 |
参数说明 |
默认值 |
示例 |
|---|---|---|---|
.group(String group) |
分组唯一标识(显示在 UI 中) |
必填 |
.group("用户管理") |
.pathsToMatch(String... paths) |
路径匹配规则(支持 Ant 风格) |
可选 |
.pathsToMatch("/api/user/**") |
.packagesToScan(String... pkgs) |
扫描的包路径 |
可选 |
.packagesToScan("com.example") |
.pathsToExclude(String... paths) |
排除的路径 |
空数组 |
.pathsToExclude("/internal/**") |
.addOpenApiMethodFilter(Predicate) |
自定义方法过滤逻辑 |
无 |
见下面示例 |
.displayName(String name) |
显示名称(覆盖 group 的显示) |
同 group 值 |
.displayName("用户模块") |
.addOperationCustomizer(...) |
自定义操作处理器 |
无 |
见下面示例 |
通过合理的分组配置,可在 JDK 17 环境下构建清晰、安全、易维护的 API 文档体系,充分发挥 SpringDoc 和 Knife4j 的现代化文档能力。
基础分组配置:
<code class="javascript">import org.springdoc.core.models.GroupedOpenApi;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configurationpublic class OpenApiGroupConfig { // 用户管理分组 @Bean public GroupedOpenApi userApi() { return GroupedOpenApi.builder() .group("用户管理") // 分组显示名称 .pathsToMatch("/api/user/**") // 路径匹配规则 .packagesToScan("com.example.user") // 包扫描路径 .build(); } // 订单管理分组 @Bean public GroupedOpenApi orderApi() { return GroupedOpenApi.builder() .group("订单管理") .pathsToMatch("/api/order/**") .packagesToScan("com.example.order") .build(); } // 分组自定义排序 @Bean public GroupedOpenApi firstGroup() { return GroupedOpenApi.builder() .group("01-核心接口") .order(1) // 分组排序(数值越小越靠前) .pathsToMatch("/core/**") .build(); } @Bean public GroupedOpenApi secondGroup() { return GroupedOpenApi.builder() .group("02-辅助接口") .order(2) .pathsToMatch("/support/**") .build(); }}</code>按安全权限分组:
<code class="javascript">@Beanpublic GroupedOpenApi adminApi() { return GroupedOpenApi.builder() .group("管理员接口") .pathsToMatch("/api/admin/**") // 只包含带有 @PreAuthorize("hasRole('ADMIN')") 的接口 .addOpenApiMethodFilter(method -> method.isAnnotationPresent(PreAuthorize.class) && method.getAnnotation(PreAuthorize.class).value().contains("ADMIN") ) .build();}</code>多版本 API 分组:
<code class="javascript">@Beanpublic GroupedOpenApi v1Api() { return GroupedOpenApi.builder() .group("API-v1") .pathsToMatch("/api/v1/**") .displayName("版本 1.0 (已弃用)") .build();}@Beanpublic GroupedOpenApi v2Api() { return GroupedOpenApi.builder() .group("API-v2") .pathsToMatch("/api/v2/**") .displayName("版本 2.0 (最新)") .build();}</code>第三方接口分组:
<code class="javascript">@Beanpublic GroupedOpenApi paymentApi() { return GroupedOpenApi.builder() .group("支付网关") .pathsToMatch("/payment/**") // 排除内部实现类 .packagesToExclude("com.example.internal.payment") .build();}</code>Ctrl+Shift+R),将以下包名替换:javax.servlet → jakarta.servletjavax.validation → jakarta.validationjavax.persistence → jakarta.persistenceMaven 插件辅助:使用 maven-replacer-plugin 自动化替换:<code class="javascript">// src/main/java/module-info.javaopen module com.example.api { requires spring.boot; requires spring.boot.autoconfigure; requires spring.web; requires springdoc.openapi.common; requires com.fasterxml.jackson.databind; exports com.example.api.controller; exports com.example.api.model;}</code>启动应用后,访问以下地址:
Knife4j UI 文档:http://localhost:8080/doc.html
OpenAPI JSON:http://localhost:8080/v3/api-docs
响应示例:
<code class="javascript">@Operation(summary = "创建用户")@ApiResponses({ @ApiResponse( responseCode = "201", content = @Content( mediaType = "application/json", schema = @Schema(implementation = User.class), examples = @ExampleObject( name = "successExample", value = """ { "id": 1, "name": "张三" } """ ) ) ), @ApiResponse( responseCode = "400", content = @Content( examples = @ExampleObject( name = "errorExample", value = """ { "code": "INVALID_REQUEST", "message": "用户名不能为空" } """ ) ) )})public ResponseEntity<User> createUser(@RequestBody User user) { ... }</code>离线文档导出:
Knife4j 导出:访问 http://localhost:8080/doc.html#/home,点击“下载 Markdown”或“下载 OpenAPI JSON”。
Type javax.servlet.http.HttpServletRequest not present
错误日志:java.lang.TypeNotPresentException: Type javax.servlet.http.HttpServletRequest not present
原因:未迁移到 Jakarta EE 包名。
解决步骤:
检查是否遗漏包名替换(使用 IDE 全局搜索 javax.servlet),更新依赖至 Jakarta 版本。全局替换代码中的javax.servlet 为 jakarta.servlet。运行 mvn dependency:tree | grep javax.servlet 确认无冲突依赖。更新第三方库至 Jakarta 兼容版本(如 Hibernate 6.x、Tomcat 10.x)。/doc.html 报 404原因:静态资源被拦截或未正确映射。
解决步骤:
确认使用的是 Knife4j OpenAPI3 的 Spring Boot Starter(knife4j-openapi3-jakarta-spring-boot-starter),而非旧版 Knife4j 或 SpringFox。检查静态资源路径(若自定义了 WebMvcConfigurer):Knife4j 的静态资源默认位于 classpath:/META-INF/resources/webjars/knife4j-openapi3-ui/,需确保资源未被拦截或覆盖。<code class="javascript">import org.springframework.context.annotation.Configuration;import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configurationpublic class WebConfig implements WebMvcConfigurer { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { // 添加 Knife4j 的静态资源映射 registry.addResourceHandler("/doc.html") .addResourceLocations("classpath:/META-INF/resources/"); registry.addResourceHandler("/webjars/**") .addResourceLocations("classpath:/META-INF/resources/webjars/"); }}</code><code class="javascript">import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.security.config.annotation.web.builders.HttpSecurity;import org.springframework.security.web.SecurityFilterChain;@Configurationpublic class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth // 放行 Knife4j 相关路径 .requestMatchers( "/doc.html", "/webjars/**", "/v3/api-docs/**", "/favicon.ico" ).permitAll() .anyRequest().authenticated() ) .csrf(csrf -> csrf.disable()); // 如果不需要 CSRF 防护 return http.build(); }}</code>检查项:
确保控制器添加@Tag,方法添加 @Operation。模型类字段未标注 @Schema。包扫描路径未覆盖(通过 @ComponentScan 或 springdoc.packagesToScan 配置)。排除旧版 Swagger 依赖冲突:mvn dependency:tree -Dincludes=io.springfox从 JDK 8 迁移到 JDK 17 不仅是版本的升级,更是技术栈向现代 Java 生态的过渡。通过 依赖替换、注解迁移、包名调整、模块化适配 四步核心操作,可高效完成 Swagger 升级。Knife4j 和 SpringDoc 的组合,不仅解决了兼容性问题,还提供了更强大的 API 文档管理能力。升级后,建议通过自动化测试和持续监控,确保系统的稳定性和可维护性。
以上就是从 JDK 8 到 JDK 17:Swagger 升级迁移指南的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号