介绍

Springfox 是一个结合 Spring Web 及 Swagger UI 的一个可视化 API 框架,自从出现了 springfox-boot-starter �后即可实现零配置的 swagger ui 可视化 API 页面。那么他是如何做到零配置的呢?了解它的实现方式对于我们来说也是极具参考价值。

API 访问地址

Springfox.SwaggerUI2 的 API 可视化访问页面地址是
http://localhost:8080/swagger-ui.html
而 Springfox.SwaggerUI3(OpenAPI) 的 API 可视化访问页面地址是 http://localhost:8080/swagger-ui/index.html 或者简化为 http://localhost:8080/swagger-ui/(请不要忽略最后一个斜杆 /)


Demo 程序

  1. `edward`
  2. |-`springfox`
  3. |-`controller`
  4. | |- DemoController.java
  5. |- BootApplication.java
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.7</version>
    </parent>

    <groupId>org.example</groupId>
    <artifactId>my-first-springfox-swagger-ui</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>

        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-boot-starter</artifactId>
            <version>3.0.0</version>
        </dependency>
    </dependencies>

</project>
package edward.springfox.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DemoController {
    @GetMapping("/hello")
    public String hello(@RequestParam(defaultValue = "World") String name) {
        return String.format("Hello %s!", name);
    }
}
package edward.springfox;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class BootApplication {

    public static void main(String[] args) {
        SpringApplication.run(BootApplication.class, args);
    }
}

运行 edward.springfox.BootApplication后访问 http://localhost:8080/swagger-ui/index.html 或者简化为 http://localhost:8080/swagger-ui/(请不要忽略最后一个斜杆 /)

源码探究集成原理

首先找到 starter 的配置文件 springfox-boot-starter-3.0.0.jar!/META-INF/spring.factories,了解过 starter 的都知道,这里配置的 AutoConfiguration 是 starter 的启动入口,是所有 Bean 加载的起源。

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
springfox.boot.starter.autoconfigure.OpenApiAutoConfiguration

1. 映射 /swagger-ui/*

模块来源: springfox-boot-starter-.jar

通过SwaggerUiWebFluxConfigurer实现静态资源映射到 classpath:/META-INF/resources/webjars/springfox-swagger-ui/ 资源地址(Jar 包内的 /META-INF/resources 文件)

package springfox.boot.starter.autoconfigure;

public class SwaggerUiWebFluxConfigurer implements WebFluxConfigurer {
  @Override
  public void addResourceHandlers(ResourceHandlerRegistry registry) {
    String baseUrl = StringUtils.trimTrailingCharacter(this.baseUrl, '/');
    registry.
        addResourceHandler(baseUrl + "/swagger-ui/**")
        .addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/")
        .resourceChain(false);
  }
}

2. 映射 /v3/api-docs

模块来源: springfox-oas-.jar

package springfox.documentation.oas.web;

@ApiIgnore
@RestController
// @RequestMapping(OPEN_API_SPECIFICATION_PATH) // 请看下一行
@RequestMapping("${springfox.documentation.open-api.v3.path:/v3/api-docs}")
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
@Conditional(OnReactiveWebApplication.class)
public class OpenApiControllerWebFlux {
  @GetMapping(
      produces = {
          APPLICATION_JSON_VALUE,
          HAL_MEDIA_TYPE})
  public ResponseEntity<Json> getDocumentation(
      @RequestParam(value = "group", required = false) String swaggerGroup,
      ServerHttpRequest serverRequest) {
      ... ...
  }
}

3. 映射 /swagger-resource/*

模块来源: springfox-swagger-common-.jar

package springfox.documentation.swagger.web;

@RestController
@ApiIgnore
@RequestMapping({"${springfox.documentation.swagger-ui.base-url:}/swagger-resources"})
public class ApiResourceController {
    @GetMapping(
        value = {"/configuration/security"},
        produces = {"application/json"}
    )
    public ResponseEntity<SecurityConfiguration> securityConfiguration() {}

    @GetMapping(
        value = {"/configuration/ui"},
        produces = {"application/json"}
    )
    public ResponseEntity<UiConfiguration> uiConfiguration() {}

    @GetMapping(
        produces = {"application/json"}
    )
    public ResponseEntity<List<SwaggerResource>> swaggerResources() {}
}

4. 简化访问首页

模块来源: springfox-boot-starter-.jar

http://localhost:8080/swagger-ui/index.html 简化为 http://localhost:8080/swagger-ui/ (请不要忽略最后一个斜杆 /)

package springfox.boot.starter.autoconfigure;

@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
@ConditionalOnProperty(
    value = "springfox.documentation.swagger-ui.enabled",
    havingValue = "true",
    matchIfMissing = true)
public class SwaggerUiWebFluxConfiguration {
  @Bean
  public WebFilter uiForwarder() {
    return new CustomWebFilter();
  }
  private static class CustomWebFilter implements WebFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
      String path = exchange.getRequest().getURI().getPath();
      if (path.matches(".*/swagger-ui/")) {
        return chain.filter(exchange.mutate().request(exchange.getRequest()
            .mutate()
            .path(StringUtils.trimTrailingCharacter(path, '/') + "/index.html")
            .build())
            .build());
      }
      return chain.filter(exchange);
    }
  }
}

5. 扫包加载 Controller

注意到 Configuration 配置类的 ComponentScan 配置 “springfox.documentation.swagger.web”“springfox.documentation.oas.web”

5.1 springfox-swagger-common-.jar

package springfox.documentation.swagger.configuration;

@Configuration
@ComponentScan(
    basePackages = {"springfox.documentation.swagger.schema",
                    "springfox.documentation.swagger.readers",
                    "springfox.documentation.swagger.web"}
)
public class SwaggerCommonConfiguration {
    public SwaggerCommonConfiguration() {
    }
}

5.2 springfox-oas-.jar

package springfox.documentation.oas.configuration;

@Configuration
@Import({
    SpringfoxWebConfiguration.class,
    SpringfoxWebMvcConfiguration.class,
    SpringfoxWebFluxConfiguration.class,
    SwaggerCommonConfiguration.class,
    OpenApiMappingConfiguration.class,
    OpenApiWebMvcConfiguration.class,
    OpenApiWebFluxConfiguration.class
})
@ComponentScan(basePackages = {
    "springfox.documentation.oas.web",
    "springfox.documentation.oas.mappers"
})
public class OpenApiDocumentationConfiguration {
}

相关链接

https://springfox.github.io/springfox/docs/current/