SpringDoc vs Swagger: Complete Spring Boot API Documentation Migration Guide
This tutorial explains why SpringDoc replaces the deprecated SpringFox for Spring Boot 2.6+/3.x, demonstrates zero-config setup with springdoc-openapi-starter-webmvc-ui 2.5.0, covers programmatic and declarative API grouping via GroupedOpenApi beans or YAML, and shows how to resolve static resource conflicts with WebMvcConfigurer and SpringSecurity permitAll rules for /swagger-ui/**, /v3/api-docs/**, and /webjars/**.
What Is SpringDoc
SpringDoc is a library built for Spring Boot that automatically generates API documentation compliant with the OpenAPI 3 specification. It scans controllers, method annotations, and configurations at runtime to produce JSON, YAML, or HTML docs and bundles Swagger UI for interactive browsing and testing.
Relationship with Swagger
Swagger originated the OpenAPI specification and provides Swagger UI for rendering. SpringDoc is not a replacement for Swagger; it is an OpenAPI 3 implementation that embeds Swagger UI as its default visualization layer.
Why SpringDoc Over SpringFox
SpringFox was the prior Spring ecosystem integration for Swagger. It performed runtime scanning of @RestController, @RequestMapping, and Swagger-specific annotations like @ApiOperation, emitted Swagger 2.0 or OpenAPI 3.0 JSON, and exposed a Docket configuration class for customizing scan paths and metadata. Swagger UI then consumed that JSON at /swagger-ui.html.
Around 2020 SpringFox maintenance effectively stopped. It cannot adapt to Spring Boot 2.6+ or 3.x (JDK 17+), causing path-matching failures and annotation incompatibilities. SpringDoc fills the gap: it supports Spring Boot 2.6+/3.x natively, adheres to OpenAPI 3 out of the box, requires only a single dependency for zero-config usage, and leverages standard JSR-303 annotations such as @Schema and @Parameter instead of SpringFox-proprietary ones like @ApiModel, reducing learning overhead.
Minimal Setup
Step 1: Add Dependency
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.5.0</version>
</dependency>Step 2: Configuration Defaults (Optional)
Without any YAML, SpringDoc scans the entire classpath for @RestController and @RequestMapping. Key defaults:
springdoc:
packages-to-scan: com.example.controller # default: full classpath
swagger-ui:
enabled: true
path: /swagger-ui/index.html
url: /v3/api-docs
disable-swagger-default-url: false
api-docs:
enabled: true
path: /v3/api-docsStep 3: Basic Info Configuration Class
@Configuration
@OpenAPIDefinition(info = @Info(
title = "项目API文档",
version = "1.0",
description = "SpringBoot项目接口文档"
))
public class SpringDocConfig {}Step 4: Access and Annotate
Visit http://localhost:8080/swagger-ui/index.html. Without controllers the page is empty. Adding OpenAPI annotations enriches the output:
@RestController
@RequestMapping("/main")
@Tag(name = "演示controller", description = "演示controller")
public class MainController {
@GetMapping("/index")
@Operation(summary = "演示方法", description = "演示方法的注释")
public String index(@Parameter(description = "参数1", required = true) String str1) {
return "请求成功";
}
}Unannotated endpoints show only path and method; annotated ones display summaries, descriptions, and parameter metadata.
API Grouping
Grouping separates endpoints into named categories in the UI dropdown.
Programmatic Configuration (High Flexibility)
@Configuration
@OpenAPIDefinition(info = @Info(title = "项目API文档", version = "1.0", description = "SpringBoot项目接口文档"))
public class SpringDocConfig {
@Bean
public GroupedOpenApi defaultGroup() {
return GroupedOpenApi.builder()
.group("默认分组")
.pathsToMatch("/**")
.build();
}
@Bean
public GroupedOpenApi userGroup() {
return GroupedOpenApi.builder()
.group("商品模块")
.pathsToMatch("/api/product/**")
.build();
}
@Bean
public GroupedOpenApi productGroup() {
return GroupedOpenApi.builder()
.group("用户模块")
.packagesToScan("com.ren.main.controller.member")
.build();
}
}Rules: once any GroupedOpenApi bean exists, springdoc.packages-to-scan in YAML is ignored because group scan paths take precedence. Two scan modes exist — path matching ( pathsToMatch) and package scanning ( packagesToScan). Overlapping paths cause an endpoint to appear in multiple groups.
The author's project structure includes MainController, ProductController under /api/product/**, and MemberController under com.ren.main.controller.member. Without a default group, MainController disappears because group config overrides global scan. Adding a default group with pathsToMatch("/**") restores it; overlapping endpoints then show in both their specific group and the default group.
Declarative Configuration (Centralized, Environment-Isolated)
springdoc:
group-configs:
- group: '默认分组'
paths-to-match: '/**'
- group: '商品模块'
paths-to-match: '/api/product/**'
- group: '用户模块'
packages-to-scan: 'com.ren.main.controller.member'Produces identical UI grouping.
Handling WebMvcConfigurer.addResourceHandlers Override
WebMvcConfigurercustomizes Spring MVC behavior (interceptors, CORS, static resources, view controllers, message converters, path matching, argument/return-value handlers, content negotiation). Overriding addResourceHandlers disables all default static resource mappings, including SpringDoc's Swagger UI assets. Restore them explicitly:
@Configuration
public class ResourcesConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/swagger-ui/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/springdoc-openapi-ui/")
.setCacheControl(CacheControl.maxAge(5, TimeUnit.HOURS).cachePublic());
}
}Note: SpringDoc 2.x resource path differs from legacy SpringFox.
SpringSecurity Integration
SpringSecurity blocks unauthenticated access by default. Permit SpringDoc endpoints in the filter chain:
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(auth -> auth
.requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.requestMatchers(request -> {
String path = request.getServletPath();
return request.getMethod().equals("GET") && (
"/".equals(path) || path.endsWith(".html") || path.endsWith(".css") || path.endsWith(".js"));
}).permitAll()
.requestMatchers(
"/swagger-ui/**",
"/*/api-docs/**",
"/swagger-resources/**",
"/webjars/**",
"/druid/**"
).permitAll()
.anyRequest().authenticated()
);
return http.build();
}
}This opens Swagger UI, the OpenAPI docs endpoint, Swagger resources, WebJars, and (optionally) Druid console while keeping all other requests secured.
Summary
The article walks through SpringDoc adoption from rationale to production-ready configuration: zero-dependency start, annotation-driven metadata, two grouping strategies with precedence rules, static resource handler conflict resolution, and SpringSecurity permit lists. All code targets SpringDoc 2.5.0 on Spring Boot 3.x.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java Tech Enthusiast
Sharing computer programming language knowledge, focusing on Java fundamentals, data structures, related tools, Spring Cloud, IntelliJ IDEA... Book giveaways, red‑packet rewards and other perks await!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
