On this page
This Was Not Just A Swagger UI Problem
Springfox can fail before Swagger UI has a chance to render. The useful debugging boundary is the request-mapping metadata that Springfox scans while the application starts, not the browser page that never loads.
The runnable lab in labs/swagger-springfox-migration-lab/ exercises the same tiny /orders/{orderId} API through three cases:
- Spring Boot 2.7.18 + Springfox 3.0.0 +
PathPatternParser. - Spring Boot 2.7.18 + Springfox 3.0.0 +
AntPathMatcherplus a handler-mapping filter. - Spring Boot 3.4.7 +
springdoc-openapi-starter-webmvc-ui2.8.17, grouped docs, and bearer-token OpenAPI metadata.
The goal is not to argue that one documentation library is always better. The goal is to make a version-boundary failure observable, then choose the smallest responsible fix for each application generation.
The Failure
The failure looked like a Swagger parse error, but the important line was lower in the stack trace:
Failed to start bean 'documentationPluginsBootstrapper';
nested exception is java.lang.NullPointerException:
Cannot invoke "org.springframework.web.servlet.mvc.condition.PatternsRequestCondition.getPatterns()"
because "this.condition" is null
at springfox.documentation.spring.web.WebMvcPatternsRequestConditionWrapper.getPatterns
at springfox.documentation.RequestHandler.sortedPaths
at springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider.requestHandlers
That tells us Springfox is not merely failing to render UI resources. It is failing while scanning Spring MVC request mappings and sorting discovered paths before the docs endpoint can be served.
The reproduced evidence is saved in /resources/blog/post-50-swagger/springfox-default-path-pattern-failure.txt.
Why Path Matching Matters
Spring MVC has two path-matching models that matter here:
| Model | Rough shape | Why it matters for Springfox |
|---|---|---|
AntPathMatcher | String-pattern based matching | Older libraries often expect PatternsRequestCondition to be populated |
PathPatternParser | Parsed path-pattern matching | Request mapping metadata can be represented without the old condition object Springfox expects |
Spring Framework’s path matching documentation describes PathPatternParser as the parsed-pattern alternative to AntPathMatcher. Spring Boot’s 2.7 web documentation explains the matching-strategy property. A Spring Boot 2.6 documentation issue also records that the MVC auto-configuration default was actually path-pattern-parser in that line.
Springfox 3.0.0 was built around older Spring MVC assumptions. Its own Spring Boot application notes mainly describe dependency and annotation changes for Springfox 3.x. They do not turn Springfox into a modern Spring Boot 3 documentation stack.
That is why “Swagger broke” is too vague. The actual bug is:
Springfox’s request-handler scanner expects mapping metadata that is not available in the same shape when Spring MVC uses parsed path patterns.
Lab Design
The lab uses Maven inside a JDK 17 Docker container:
services:
maven:
image: maven:3.9.9-eclipse-temurin-17
working_dir: /workspace
volumes:
- .:/workspace
- maven-cache:/root/.m2
There are two modules:
springfox-boot27
springdoc-boot3
Both expose the same API:
@RestController
class OrderController {
@GetMapping("/orders/{orderId}")
OrderResponse getOrder(@PathVariable String orderId) {
return new OrderResponse(orderId, "PAID", 42000);
}
}
Run it:
cd labs/swagger-springfox-migration-lab
./scripts/run-matrix.sh
The successful rerun used ID 20260710T024421Z.
Observed Matrix
The clean run produced this result:
| Scenario | Endpoint | Observed outcome |
|---|---|---|
Boot 2.7.18 + Springfox 3.0.0 + PathPatternParser | /v2/api-docs | application startup failed before the endpoint could be served |
Boot 2.7.18 + Springfox 3.0.0 + AntPathMatcher + handler-mapping filter | /v2/api-docs | returned a Swagger 2 document |
| Boot 3.4.7 + springdoc-openapi 2.8.17 | /v3/api-docs and /v3/api-docs/orders | returned OpenAPI 3.1 documents with bearer auth metadata |
The generated summary is saved at /resources/blog/post-50-swagger/matrix-summary.md.
The positive Springfox case returned:
Scenario: Spring Boot 2.7.18 + Springfox 3.0.0 + AntPathMatcher + handler-mapping filter
Outcome: /v2/api-docs returned a Swagger 2 document
Contains swagger 2 marker: true
Contains /orders/{orderId}: true
Contains OrderResponse schema: true
The springdoc migration case returned:
Scenario: Spring Boot 3.4.7 + springdoc-openapi-starter-webmvc-ui 2.8.17
Outcome: /v3/api-docs returned an OpenAPI document
HTTP status: 200 OK
Contains openapi marker: true
Contains /orders/{orderId}: true
Contains OrderResponse schema: true
Contains bearerAuth security scheme: true
Grouped endpoint /v3/api-docs/orders status: 200 OK
Unauthenticated GET /orders/demo-1 status: 403 FORBIDDEN
Those two lines are more important than a Swagger UI screenshot. They prove that the generated machine-readable contract exists and includes the expected operation and schema.
Boot 2.7: If You Cannot Migrate Immediately
For an existing Spring Boot 2.7 service that still uses Springfox, the compatibility path in the lab is explicit:
spring.mvc.pathmatch.matching-strategy=ant-path-matcher
lab.springfox.compatibility-filter=true
The handler-mapping filter is intentionally isolated behind a property:
@Configuration
@ConditionalOnProperty(name = "lab.springfox.compatibility-filter", havingValue = "true")
class SpringfoxCompatibilityConfig {
@Bean
static BeanPostProcessor springfoxHandlerMappingFilter() {
return new BeanPostProcessor() {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if ("springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider"
.equals(bean.getClass().getName())) {
filterParsedPatternMappings(bean);
}
return bean;
}
};
}
}
The real implementation in the lab reflects into Springfox’s handlerMappings list and removes mappings whose getPatternParser() is not null. This is a compatibility patch, not a clean architecture pattern. It depends on Springfox internals and should be covered by an integration test that actually calls /v2/api-docs.
My rule after reproducing this is:
- If the application is staying on Boot 2 for a while, keep the workaround narrow and tested.
- If the application is moving to Boot 3, do not carry Springfox forward.
Boot 3: Migrate The Documentation Stack
Spring Boot 3 moved to Jakarta namespaces and a newer Spring Framework baseline. Treating Springfox as something to patch through that migration is the wrong default.
The springdoc path in the lab uses:
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.8.17</version>
</dependency>
The migrated module also adds spring-boot-starter-security, a SecurityFilterChain, a grouped OpenAPI bean, and a bearer-token security scheme:
@Configuration
@SecurityScheme(
name = "bearerAuth",
type = SecuritySchemeType.HTTP,
scheme = "bearer",
bearerFormat = "JWT"
)
class OpenApiConfig {
@Bean
GroupedOpenApi ordersApi() {
return GroupedOpenApi.builder()
.group("orders")
.pathsToMatch("/orders/**")
.build();
}
}
The security filter keeps generated docs reachable while protecting the API endpoint:
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(auth -> auth
.requestMatchers("/v3/api-docs/**", "/swagger-ui/**", "/swagger-ui.html").permitAll()
.anyRequest().authenticated()
)
.build();
}
The springdoc documentation says this starter exposes Swagger UI and the OpenAPI JSON endpoint for Spring Boot applications. In the lab, /v3/api-docs returned:
{
"openapi": "3.1.0",
"paths": {
"/orders/{orderId}": {
"get": {
"summary": "Find one order"
}
}
}
}
The important migration difference is not just endpoint naming:
| Springfox path | springdoc path |
|---|---|
/v2/api-docs | /v3/api-docs |
| Swagger 2 model by default | OpenAPI 3.x model |
Docket configuration | starter auto-configuration plus @Operation, @Tag, GroupedOpenApi |
| Boot 2 compatibility workaround may be needed | Boot 3-compatible library line |
| Ad hoc security snippets | explicit docs allowlist plus OpenAPI bearerAuth scheme |
If the frontend, QA tooling, or API gateway still expects /v2/api-docs, the migration must include that consumer contract. Do not silently change the docs endpoint and call the backend migration done.
Security Configuration Is A Separate Test
Swagger endpoints also need an explicit Spring Security decision. A Boot 2-era allowlist often looks like this:
.antMatchers("/swagger-ui/**", "/v3/api-docs", "/v2/api-docs").permitAll()
That line is version-sensitive:
- Boot 2 / Spring Security 5 commonly used
antMatchers. - Boot 3 / Spring Security 6 uses the newer
authorizeHttpRequestsstyle andrequestMatchers.
More importantly, a docs endpoint should not automatically be public in every environment. For internal admin APIs, it may be better to expose docs only behind VPN, SSO, basic auth, or a non-production profile.
The lab now turns that into a regression test:
GET /v3/api-docs as an unauthenticated user
expected: 200 OK
GET /orders/demo-1 as an unauthenticated user
expected: 403 FORBIDDEN
That test tells you more than a copied allowlist snippet.
Debugging Checklist
When Swagger/OpenAPI generation breaks after a Spring Boot upgrade, check it in this order:
-
Identify the docs library and version.
Springfox, springdoc, and raw Swagger UI are not interchangeable. The failure mode depends on which library is scanning Spring MVC mappings.
-
Find the endpoint that should exist.
For Springfox it is usually
/v2/api-docs. For springdoc it is usually/v3/api-docs. Test the JSON endpoint before debugging the UI. -
Read the stack trace at the scanner layer.
If the stack contains
documentationPluginsBootstrapper,WebMvcRequestHandlerProvider, orWebMvcPatternsRequestConditionWrapper, suspect Springfox and MVC path matching. -
Check Spring MVC path matching.
If you are on Boot 2.6 or 2.7 and still using Springfox, compare
path-pattern-parserandant-path-matcherwith an integration test. -
Separate compatibility from migration.
A Boot 2 compatibility patch may be acceptable as a temporary hold. It should not become the Boot 3 migration strategy.
-
Verify security and deployment behavior.
The JSON docs endpoint, the Swagger UI static assets, reverse-proxy path rewriting, and auth filters can fail independently.
What This Proves In An Interview
This lab is small, but it demonstrates a better debugging habit than “I followed a Swagger setup tutorial.”
It proves that I can:
- Reproduce a framework compatibility failure with a minimal app.
- Turn a stack trace into a hypothesis about request-mapping internals.
- Separate UI rendering from generated API contract availability.
- Keep a risky workaround contained and tested.
- Define a migration path that accounts for consumers of
/v2/api-docsand/v3/api-docs.
The practical conclusion is direct:
For existing Boot 2.7 services, patch Springfox only as a tested bridge. For Boot 3 services, migrate to springdoc and verify
/v3/api-docsas part of the upgrade.
That is the engineering decision hidden behind what first looked like a Swagger parse error.