Backend Development 12 min read

Switching to SpringDoc: A Complete Guide to Replacing SpringFox in Spring Boot

After SpringFox became outdated and incompatible with Spring Boot 2.6, this article introduces SpringDoc as a modern OpenAPI 3‑based alternative, explains its advantages, shows how to integrate it, migrate annotations, configure security, and provides sample code and screenshots for a seamless transition.

macrozheng
macrozheng
macrozheng
Switching to SpringDoc: A Complete Guide to Replacing SpringFox in Spring Boot

SpringDoc Overview

SpringDoc is an API documentation tool that works with Spring Boot, based on OpenAPI 3. It has over 1.7K stars on GitHub and is actively maintained. It supports Spring WebMvc, WebFlux, Rest, and Native projects.

Usage

We will replace the previous SpringFox integration in the mall-tiny-swagger project with SpringDoc.

Integration

Add the SpringDoc starter dependency to pom.xml . No additional configuration is required.
<code><!--springdoc 官方Starter-->
<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-ui</artifactId>
    <version>1.6.6</version>
</dependency>
</code>

Migrating from SpringFox

Compare common Swagger annotations between SpringFox and SpringDoc.

SpringFox

SpringDoc

@Api

@Tag

@ApiIgnore

@Parameter(hidden = true) or @Operation(hidden = true) or @Hidden

@ApiImplicitParam

@Parameter

@ApiImplicitParams

@Parameters

@ApiModel

@Schema

@ApiModelProperty

@Schema

@ApiOperation(value = "foo", notes = "bar")

@Operation(summary = "foo", description = "bar")

@ApiParam

@Parameter

@ApiResponse(code = 404, message = "foo")

ApiResponse(responseCode = "404", description = "foo")

Update controller annotations according to the table; for example, the deprecated

@Api

description attribute is now supported via

@Tag

description.

<code>/**
 * 品牌管理Controller
 * Created by macro on 2019/4/19.
 */
@Tag(name = "PmsBrandController", description = "商品品牌管理")
@Controller
@RequestMapping("/brand")
public class PmsBrandController {
    @Autowired
    private PmsBrandService brandService;
    private static final Logger LOGGER = LoggerFactory.getLogger(PmsBrandController.class);

    @Operation(summary = "获取所有品牌列表", description = "需要登录后访问")
    @RequestMapping(value = "listAll", method = RequestMethod.GET)
    @ResponseBody
    public CommonResult<List<PmsBrand>> getBrandList() {
        return CommonResult.success(brandService.listAllBrand());
    }

    @Operation(summary = "添加品牌")
    @RequestMapping(value = "/create", method = RequestMethod.POST)
    @ResponseBody
    @PreAuthorize("hasRole('ADMIN')")
    public CommonResult createBrand(@RequestBody PmsBrand pmsBrand) {
        CommonResult commonResult;
        int count = brandService.createBrand(pmsBrand);
        if (count == 1) {
            commonResult = CommonResult.success(pmsBrand);
            LOGGER.debug("createBrand success:{}", pmsBrand);
        } else {
            commonResult = CommonResult.failed("操作失败");
            LOGGER.debug("createBrand failed:{}", pmsBrand);
        }
        return commonResult;
    }

    @Operation(summary = "更新指定id品牌信息")
    @RequestMapping(value = "/update/{id}", method = RequestMethod.POST)
    @ResponseBody
    @PreAuthorize("hasRole('ADMIN')")
    public CommonResult updateBrand(@PathVariable("id") Long id, @RequestBody PmsBrand pmsBrandDto, BindingResult result) {
        CommonResult commonResult;
        int count = brandService.updateBrand(id, pmsBrandDto);
        if (count == 1) {
            commonResult = CommonResult.success(pmsBrandDto);
            LOGGER.debug("updateBrand success:{}", pmsBrandDto);
        } else {
            commonResult = CommonResult.failed("操作失败");
            LOGGER.debug("updateBrand failed:{}", pmsBrandDto);
        }
        return commonResult;
    }

    @Operation(summary = "删除指定id的品牌")
    @RequestMapping(value = "/delete/{id}", method = RequestMethod.GET)
    @ResponseBody
    @PreAuthorize("hasRole('ADMIN')")
    public CommonResult deleteBrand(@PathVariable("id") Long id) {
        int count = brandService.deleteBrand(id);
        if (count == 1) {
            LOGGER.debug("deleteBrand success :id={}", id);
            return CommonResult.success(null);
        } else {
            LOGGER.debug("deleteBrand failed :id={}", id);
            return CommonResult.failed("操作失败");
        }
    }

    @Operation(summary = "分页查询品牌列表")
    @RequestMapping(value = "/list", method = RequestMethod.GET)
    @ResponseBody
    @PreAuthorize("hasRole('ADMIN')")
    public CommonResult<CommonPage<PmsBrand>> listBrand(@RequestParam(value = "pageNum", defaultValue = "1")
                                                         @Parameter(description = "页码") Integer pageNum,
                                                         @RequestParam(value = "pageSize", defaultValue = "3")
                                                         @Parameter(description = "每页数量") Integer pageSize) {
        List<PmsBrand> brandList = brandService.listBrand(pageNum, pageSize);
        return CommonResult.success(CommonPage.restPage(brandList));
    }

    @Operation(summary = "获取指定id的品牌详情")
    @RequestMapping(value = "/{id}", method = RequestMethod.GET)
    @ResponseBody
    @PreAuthorize("hasRole('ADMIN')")
    public CommonResult<PmsBrand> brand(@PathVariable("id") Long id) {
        return CommonResult.success(brandService.getBrand(id));
    }
}
</code>

SpringDoc Configuration

Configure basic OpenAPI information and group APIs using

OpenAPI

and

GroupedOpenApi

.

<code>@Configuration
public class SpringDocConfig {
    @Bean
    public OpenAPI mallTinyOpenAPI() {
        return new OpenAPI()
                .info(new Info().title("Mall-Tiny API")
                        .description("SpringDoc API 演示")
                        .version("v1.0.0")
                        .license(new License().name("Apache 2.0").url("https://github.com/macrozheng/mall-learning")))
                .externalDocs(new ExternalDocumentation()
                        .description("SpringBoot 实战电商项目mall(50K+Star)全套文档")
                        .url("http://www.macrozheng.com"));
    }

    @Bean
    public GroupedOpenApi publicApi() {
        return GroupedOpenApi.builder()
                .group("brand")
                .pathsToMatch("/brand/**")
                .build();
    }

    @Bean
    public GroupedOpenApi adminApi() {
        return GroupedOpenApi.builder()
                .group("admin")
                .pathsToMatch("/admin/**")
                .build();
    }
}
</code>

Using with Spring Security

Allow Swagger resources in the security filter chain and configure JWT authentication for the API docs.

<code>@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity.csrf().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                .authorizeRequests()
                .antMatchers(HttpMethod.GET,
                        "/", "/swagger-ui.html", "/swagger-ui/", "/*.html", "/favicon.ico",
                        "/**/*.html", "/**/*.css", "/**/*.js", "/swagger-resources/**", "/v3/api-docs/**")
                .permitAll()
                .antMatchers("/admin/login").permitAll()
                .antMatchers(HttpMethod.OPTIONS).permitAll()
                .anyRequest().authenticated();
    }
}
</code>

Testing

Run the application and open the Swagger UI at

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

. Log in to obtain a JWT token, then click the "Authorize" button and paste the token without the "bearer" prefix to access secured endpoints.

Common Configuration

Additional SpringDoc properties can be set in

application.yml

, such as custom UI path, enabling/disabling docs, packages to scan, and paths to match.

<code>springdoc:
  swagger-ui:
    path: /swagger-ui.html
    enabled: true
  api-docs:
    path: /v3/api-docs
    enabled: true
  packages-to-scan: com.macro.mall.tiny.controller
  paths-to-match: /brand/**,/admin/**
</code>

Conclusion

When SpringFox becomes stale, migrating to SpringDoc offers a more up‑to‑date, feature‑rich solution with a low learning curve and support for WebFlux and other projects.

Spring BootAPI DocumentationSwaggerOpenAPISpringDoc
macrozheng
Written by

macrozheng

Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.