文章目录
一、源码
不废话,直接上代码
1.1 pom.xml
版本定义在父工程了
- SpringBoot 2.6.5
- springfox-boot-starter 3.0.0
- knife4j-spring-boot-starter 3.0.3
<!--提供SpringMVC支持-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--监控、健康检查-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--hibernate-validator-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!--json模块-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
</dependency>
<!--swagger 依赖-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<exclusions>
<!--spring-plugin-core版本太低,排除掉-->
<exclusion>
<groupId>org.springframework.plugin</groupId>
<artifactId>spring-plugin-core</artifactId>
</exclusion>
<exclusion>
<artifactId>swagger-models</artifactId>
<groupId>io.swagger</groupId>
</exclusion>
<exclusion>
<artifactId>swagger-annotations</artifactId>
<groupId>io.swagger</groupId>
</exclusion>
</exclusions>
</dependency>
<!--依赖更高版本的spring-plugin-core-->
<dependency>
<groupId>org.springframework.plugin</groupId>
<artifactId>spring-plugin-core</artifactId>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
</dependency>
1.2 SwaggerAutoConfiguration
@Slf4j
@Configuration
//开启swagger,当前版本为3,替代@EnableSwagger2,实测发现不加也行
@EnableOpenApi
//或者直接省略prefix,那么直接写为swagger.enabled,当配置中存在swagger.enabled生效,matchIfMissing = true默认生效
@ConditionalOnProperty(prefix = "swagger", name = "enabled", matchIfMissing = true)
public class SwaggerAutoConfiguration {
//默认的排除路径,排除Spring Boot默认的错误处理路径和端点(在解析的url规则之上) /*/error,由于服务通常加前缀,所以前面/*忽略前缀
private static final List<String> DEFAULT_EXCLUDE_PATH = Arrays.asList("/error", "/actuator/**", "/*/error");
//swagger会解析的url规则
private static final String BASE_PATH = "/**";
@Autowired
private SwaggerProperties swaggerProperties;
@Bean
public Docket createRestApi() {
// base-path处理
if (swaggerProperties.getBasePath().isEmpty()) {
swaggerProperties.getBasePath().add(BASE_PATH);
}
// exclude-path处理
if (swaggerProperties.getExcludePath().isEmpty()) {
swaggerProperties.getExcludePath().addAll(DEFAULT_EXCLUDE_PATH);
}
//需要排除的url
List<Predicate<String>> excludePath = new ArrayList<>();
swaggerProperties.getExcludePath().forEach(path -> excludePath.add(PathSelectors.ant(path)));
ApiSelectorBuilder builder = new Docket(DocumentationType.OAS_30)
.host(swaggerProperties.getHost())
.apiInfo(apiInfo())
.select()
//此包路径下的类,才生成接口文档
.apis(RequestHandlerSelectors.basePackage(swaggerProperties.getBasePackage()));
swaggerProperties.getBasePath().forEach(p -> builder.paths(PathSelectors.ant(p)));
swaggerProperties.getExcludePath().forEach(p -> builder.paths(PathSelectors.ant(p).negate()));
return builder.build()
.securitySchemes(Collections.singletonList(securitySchemes()))
.securityContexts(Collections.singletonList(securityContexts())).pathMapping("/");
}
/**
* 配置默认的全局鉴权策略的开关,通过正则表达式进行匹配;默认匹配所有URL
*/
private SecurityContext securityContexts() {
return SecurityContext.builder().securityReferences(Collections.singletonList(this.defaultAuth()))
.forPaths(PathSelectors.regex("^(?!auth).*$")).build();
}
/**
* 默认的全局鉴权策略
*/
private SecurityReference defaultAuth() {
AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
AuthorizationScope[] authorizationScopes = new AuthorizationScope[]{authorizationScope};
return new SecurityReference("Authorization", authorizationScopes);
}
private ApiKey securitySchemes() {
return new ApiKey("Authorization", "Authorization", "header");
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title(swaggerProperties.getTitle())
.description(swaggerProperties.getDescription())
.version(swaggerProperties.getVersion())
.license(swaggerProperties.getLicense())
.licenseUrl(swaggerProperties.getLicenseUrl())
.termsOfServiceUrl(swaggerProperties.getTermsOfServiceUrl())
.contact(new Contact(
swaggerProperties.getContact().getName(),
swaggerProperties.getContact().getUrl(),
swaggerProperties.getContact().getEmail()
))
.build();
}
/**
* 解决SpringBoot2.6.x与Swagger3、knife4j3的兼容问题,以下配置也是必须的
* spring:
* mvc:
* pathmatch:
* matching-strategy: ant_path_matcher
*/
@Bean
public static BeanPostProcessor springFoxHandlerProviderBeanPostProcessor() {
return new BeanPostProcessor() {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof WebMvcRequestHandlerProvider) {
customizeSpringFoxHandlerMappings(getHandlerMappings(bean));
}
return bean;
}
private <T extends RequestMappingInfoHandlerMapping> void customizeSpringFoxHandlerMappings(List<T> mappings) {
List<T> copy = mappings.stream()
.filter(mapping -> mapping.getPatternParser() == null)
.collect(Collectors.toList());
mappings.clear();
mappings.addAll(copy);
}
@SuppressWarnings("unchecked")
private List<RequestMappingInfoHandlerMapping> getHandlerMappings(Object bean) {
try {
Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings");
field.setAccessible(true);
return (List<RequestMappingInfoHandlerMapping>) field.get(bean);
} catch (IllegalArgumentException | IllegalAccessException e) {
throw new IllegalStateException(e);
}
}
};
}
}
1.3 SwaggerProperties
@Data
@Configuration
@ConfigurationProperties("swagger")
public class SwaggerProperties {
/**
* 是否开启swagger
*/
private Boolean enabled;
/**
* swagger会解析的包路径
**/
private String basePackage = "";
/**
* swagger会解析的url规则
**/
private List<String> basePath = new ArrayList<>();
/**
* 在basePath基础上需要排除的url规则
**/
private List<String> excludePath = new ArrayList<>();
/**
* 需要排除的服务
*/
private List<String> ignoreProviders = new ArrayList<>();
/**
* 标题
**/
private String title = "";
/**
* 描述
**/
private String description = "";
/**
* 版本
**/
private String version = "";
/**
* 许可证
**/
private String license = "";
/**
* 许可证URL
**/
private String licenseUrl = "";
/**
* 服务条款URL
**/
private String termsOfServiceUrl = "";
/**
* host信息
**/
private String host = "";
/**
* 联系人信息
*/
private Contact contact = new Contact();
/**
* 全局统一鉴权配置
**/
private Authorization authorization = new Authorization();
@Data
@NoArgsConstructor
public static class Contact {
/**
* 联系人
**/
private String name = "";
/**
* 联系人url
**/
private String url = "";
/**
* 联系人email
**/
private String email = "";
}
@Data
@NoArgsConstructor
public static class Authorization {
/**
* 鉴权策略ID,需要和SecurityReferences ID保持一致
*/
private String name = "";
/**
* 需要开启鉴权URL的正则
*/
private String authRegex = "^.*$";
/**
* 鉴权作用域列表
*/
private List<AuthorizationScope> authorizationScopeList = new ArrayList<>();
private List<String> tokenUrlList = new ArrayList<>();
}
@Data
@NoArgsConstructor
public static class AuthorizationScope {
/**
* 作用域名称
*/
private String scope = "";
/**
* 作用域描述
*/
private String description = "";
}
}
1.4 application.yml
spring:
application:
name: user-center
# 修改路径匹配基于AntPathMatcher,以使用swagger
mvc:
pathmatch:
matching-strategy: ant_path_matcher
swagger:
basePackage: xx.xx自定义,也可去掉,默认读取所有
title: usercenter-api
description: "用户中心api"
license: Powered By yex
licenseUrl: https://demo.com/
terms-of-service-url: https://demo.com/
contact:
email: xxx@qq.com
url: https://demo.com/about.html
#以下配置是在网关聚合多个微服务的api文档时用的
authorization:
name: auth-center
auth-regex: ^.*$
authorization-scope-list:
- scope: server
description: server all
token-url-list:
- http://${GATEWAY-HOST:gateway-center}:${GATEWAY-PORT:8081}/auth/oauth/token
1.5 Controller
使用@Api、@ApiOperation依然有效,也可以使用新版的@Tag、@Operation搭配,这块还是用旧的熟悉一点。
@Api(tags = "用户信息管理")
@RestController
@RequestMapping("/usercenter/user")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/getUserById")
@ApiOperation("根据ID获取用户")
public UserVO getUserById(@RequestParam String id) {
return userService.getUserById(id);
}
}
二、遇到的问题
2.1 启动报错
启动报错,错误信息
Failed to start bean ‘documentationPluginsBootstrapper’; nested exception is java.lang.NullPointerException
解决方案:
参考Knife4j配置,代码已在上文给出,有很多文章说加配置spring.mvc.pathmatch.matching-strategy=ant_path_matcher就行,我遇到的情况就不行,查到资料说是因为依赖了spring-boot-starter-actuator,只能多配置一个Bean springfoxHandlerProviderBeanPostProcessor,配完后又出现依赖冲突,之后再解决冲突(pom排查冲突包),就正常启动了。
2.2 /swagger-ui/index.html报错
启动之后/swagger-ui/index.html报错,错误信息:
Unable to render this definition
The provided definition does not specify a valid version field.
Please indicate a valid Swagger or OpenAPI version field. Supported version fields are swagger: “2.0” and those that match openapi: 3.0.n (for example, openapi: 3.0.0).
解决方案:
ResponseBodyAdvice使用有讲
2.3 接口文档出不来
不报错之后发现接口文档出不来,反复测试发现spring.mvc.pathmatch.matching-strategy=ant_path_matcher这个配置也是必须的
三、正常情况
看似简单的集成,花了三天时间才搞定,记录一下踩过的坑,希望大家能避开