1、SpringBoot快速整合Swagger3.0

2、微服务整合Swagger3.0 - 抽取为公共模块

3、微服务整合Swagger3.0 - 网关Gateway聚合接口

4、微服务整合Swagger3.0 - 使用方法

5、微服务Swagger3.0升级为Knife4j

背景:快速整合Swagger的方式,需要每个微服务都创建一个SwaggerConfig类,当微服务特别多的时候,这样就不合适了;我们可以将swagger这个配置,抽取成一个公共模块。

一、实现的效果

目录

二、整合步骤

1、创建xxx-common-swagger模块

2、引入依赖

org.springframework.boot

spring-boot-starter-web

io.springfox

springfox-swagger2

${swagger.fox.version}

io.springfox

springfox-swagger-ui

${swagger.fox.version}

3、编写配置类

(1)自定义注解

/**

* EnableCustomSwagger2 自定义注解,哪个服务需要swagger2,就添加在哪个的启动类上

* @author cch

* @create 2024-01-25 17:56

*/

@Target({ ElementType.TYPE })

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Inherited

@Import({ SwaggerAutoConfiguration.class })

public @interface EnableCustomSwagger2 {

}

(2)swagger的配置类一

package com.cch.swagger.config;

import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;

import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;

import org.springframework.boot.context.properties.EnableConfigurationProperties;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.context.annotation.Import;

import springfox.documentation.builders.ApiInfoBuilder;

import springfox.documentation.builders.PathSelectors;

import springfox.documentation.builders.RequestHandlerSelectors;

import springfox.documentation.service.*;

import springfox.documentation.spi.DocumentationType;

import springfox.documentation.spi.service.contexts.SecurityContext;

import springfox.documentation.spring.web.plugins.ApiSelectorBuilder;

import springfox.documentation.spring.web.plugins.Docket;

import springfox.documentation.swagger2.annotations.EnableSwagger2;

import java.util.ArrayList;

import java.util.Arrays;

import java.util.List;

import java.util.function.Predicate;

/**

* @author cch

* @create 2024-01-25 17:57

*/

@Configuration

@EnableSwagger2

@EnableConfigurationProperties(SwaggerProperties.class)

@ConditionalOnProperty(name = "swagger.enabled", matchIfMissing = true)

//@Import({SwaggerBeanPostProcessor.class, SwaggerWebConfiguration.class})

@Import({SwaggerWebConfiguration.class})

public class SwaggerAutoConfiguration {

/**

* 默认的排除路径,排除Spring Boot默认的错误处理路径和端点

*/

private static final List DEFAULT_EXCLUDE_PATH = Arrays.asList("/error", "/actuator/**");

private static final String BASE_PATH = "/**";

@Bean

@ConditionalOnMissingBean

public SwaggerProperties swaggerProperties()

{

return new SwaggerProperties();

}

@Bean

public Docket api(SwaggerProperties swaggerProperties)

{

// base-path处理

if (swaggerProperties.getBasePath().isEmpty())

{

swaggerProperties.getBasePath().add(BASE_PATH);

}

// noinspection unchecked 未检查

List> basePath = new ArrayList>();

swaggerProperties.getBasePath().forEach(path -> basePath.add(PathSelectors.ant(path)));

// exclude-path处理

if (swaggerProperties.getExcludePath().isEmpty())

{

swaggerProperties.getExcludePath().addAll(DEFAULT_EXCLUDE_PATH);

}

List> excludePath = new ArrayList<>();

swaggerProperties.getExcludePath().forEach(path -> excludePath.add(PathSelectors.ant(path)));

ApiSelectorBuilder builder = new Docket(DocumentationType.SWAGGER_2)// DocumentationType.SWAGGER_2 固定的,代表swagger2

.host(swaggerProperties.getHost())

.apiInfo(apiInfo(swaggerProperties)) // 用于生成API信息

.select() // select()函数返回一个ApiSelectorBuilder实例,用来控制接口被swagger做成文档,select()方法配置怎么扫描接口

.apis(RequestHandlerSelectors.basePackage(swaggerProperties.getBasePackage())); // RequestHandlerSelectors配置如何扫描接口,用于指定扫描哪个包下的接口

// 配置如何通过 path过滤 即这里只扫描 请求以 /user开头的接口

// .paths(PathSelectors.ant("/user/**"))

swaggerProperties.getBasePath().forEach(p -> builder.paths(PathSelectors.ant(p))); //选择所有的API,如果你想只为部分的API生成文档,可以配置这里

//在basePath基础上需要排除的url规则

swaggerProperties.getExcludePath().forEach(p -> builder.paths(PathSelectors.ant(p).negate()));

return builder.build().securitySchemes(securitySchemes()).securityContexts(securityContexts()).pathMapping("/");

}

/**

* 安全模式,这里指定token通过Authorization头请求头传递

*/

private List securitySchemes()

{

List apiKeyList = new ArrayList();

apiKeyList.add(new ApiKey("Authorization", "Authorization", "header"));

return apiKeyList;

}

/**

* 安全上下文

*/

private List securityContexts()

{

List securityContexts = new ArrayList<>();

securityContexts.add(

SecurityContext.builder()

.securityReferences(defaultAuth())

.operationSelector(o -> o.requestMappingPattern().matches("/.*"))

.build());

return securityContexts;

}

/**

* 默认的全局鉴权策略

*

* @return

*/

private List defaultAuth()

{

AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");

AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];

authorizationScopes[0] = authorizationScope;

List securityReferences = new ArrayList<>();

securityReferences.add(new SecurityReference("Authorization", authorizationScopes));

return securityReferences;

}

//配置swagger-ui的api信息

private ApiInfo apiInfo(SwaggerProperties swaggerProperties)

{

return new ApiInfoBuilder()

.title(swaggerProperties.getTitle())

.description(swaggerProperties.getDescription())

.license(swaggerProperties.getLicense())

.licenseUrl(swaggerProperties.getLicenseUrl())

.termsOfServiceUrl(swaggerProperties.getTermsOfServiceUrl())

.contact(new Contact(swaggerProperties.getContact().getName(), swaggerProperties.getContact().getUrl(), swaggerProperties.getContact().getEmail()))

.version(swaggerProperties.getVersion())

.build();

}

}

(3)swagger的配置类二

package com.cch.swagger.config;

import org.springframework.boot.context.properties.ConfigurationProperties;

import org.springframework.stereotype.Component;

import java.util.ArrayList;

import java.util.List;

/**

* @author cch

* @create 2024-01-25 18:05

*/

@Component

@ConfigurationProperties("swagger")

public class SwaggerProperties {

/**

* 是否开启swagger

*/

private Boolean enabled;

/**

* swagger会解析的包路径

**/

private String basePackage = "";

/**

* swagger会解析的url规则

**/

private List basePath = new ArrayList<>();

/**

* 在basePath基础上需要排除的url规则

**/

private List excludePath = 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();

public Boolean getEnabled()

{

return enabled;

}

public void setEnabled(Boolean enabled)

{

this.enabled = enabled;

}

public String getBasePackage()

{

return basePackage;

}

public void setBasePackage(String basePackage)

{

this.basePackage = basePackage;

}

public List getBasePath()

{

return basePath;

}

public void setBasePath(List basePath)

{

this.basePath = basePath;

}

public List getExcludePath()

{

return excludePath;

}

public void setExcludePath(List excludePath)

{

this.excludePath = excludePath;

}

public String getTitle()

{

return title;

}

public void setTitle(String title)

{

this.title = title;

}

public String getDescription()

{

return description;

}

public void setDescription(String description)

{

this.description = description;

}

public String getVersion()

{

return version;

}

public void setVersion(String version)

{

this.version = version;

}

public String getLicense()

{

return license;

}

public void setLicense(String license)

{

this.license = license;

}

public String getLicenseUrl()

{

return licenseUrl;

}

public void setLicenseUrl(String licenseUrl)

{

this.licenseUrl = licenseUrl;

}

public String getTermsOfServiceUrl()

{

return termsOfServiceUrl;

}

public void setTermsOfServiceUrl(String termsOfServiceUrl)

{

this.termsOfServiceUrl = termsOfServiceUrl;

}

public String getHost()

{

return host;

}

public void setHost(String host)

{

this.host = host;

}

public Contact getContact()

{

return contact;

}

public void setContact(Contact contact)

{

this.contact = contact;

}

public Authorization getAuthorization()

{

return authorization;

}

public void setAuthorization(Authorization authorization)

{

this.authorization = authorization;

}

// 作者信息

public static class Contact

{

/**

* 联系人

**/

private String name = "";

/**

* 联系人url

**/

private String url = "";

/**

* 联系人email

**/

private String email = "";

public String getName()

{

return name;

}

public void setName(String name)

{

this.name = name;

}

public String getUrl()

{

return url;

}

public void setUrl(String url)

{

this.url = url;

}

public String getEmail()

{

return email;

}

public void setEmail(String email)

{

this.email = email;

}

}

public static class Authorization

{

/**

* 鉴权策略ID,需要和SecurityReferences ID保持一致

*/

private String name = "";

/**

* 需要开启鉴权URL的正则

*/

private String authRegex = "^.*$";

/**

* 鉴权作用域列表

*/

private List authorizationScopeList = new ArrayList<>();

private List tokenUrlList = new ArrayList<>();

public String getName()

{

return name;

}

public void setName(String name)

{

this.name = name;

}

public String getAuthRegex()

{

return authRegex;

}

public void setAuthRegex(String authRegex)

{

this.authRegex = authRegex;

}

public List getAuthorizationScopeList()

{

return authorizationScopeList;

}

public void setAuthorizationScopeList(List authorizationScopeList)

{

this.authorizationScopeList = authorizationScopeList;

}

public List getTokenUrlList()

{

return tokenUrlList;

}

public void setTokenUrlList(List tokenUrlList)

{

this.tokenUrlList = tokenUrlList;

}

}

public static class AuthorizationScope

{

/**

* 作用域名称

*/

private String scope = "";

/**

* 作用域描述

*/

private String description = "";

public String getScope()

{

return scope;

}

public void setScope(String scope)

{

this.scope = scope;

}

public String getDescription()

{

return description;

}

public void setDescription(String description)

{

this.description = description;

}

}

}

(4)swagger 资源映射路径

注意:3.0.0版本访问是: /swagger-ui/index.html

package com.cch.swagger.config;

import org.springframework.context.annotation.Configuration;

import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;

import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**

* swagger 资源映射路径

* @author cch

* @create 2024-01-25 21:27

*/

@Configuration

public class SwaggerWebConfiguration implements WebMvcConfigurer {

@Override

public void addResourceHandlers(ResourceHandlerRegistry registry)

{

/** swagger-ui 地址 */

//3.0.0版本访问是: /swagger-ui/index.html

registry.addResourceHandler("/swagger-ui/**")

.addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/");

}

}

(5)在 META-INF 添加spring配置

因为cch-common-swagger模块是一个公共模块,并不是一个web工程,想要让模块内的配置类生效并且加入ioc容器中,必须在resources目录下先建文件夹 META-INF,再创建 spring文件夹,然后创建org.springframework.boot.autoconfigure.AutoConfiguration.imports,内容如下:

com.cch.swagger.config.SwaggerAutoConfiguration

com.cch.swagger.config.SwaggerWebConfiguration

#com.cch.swagger.config.SwaggerBeanPostProcessor

======如果SpringBoot 版本在2.6以下,配置到此结束!!!======

三、其它

以下是Swagger3.0.0在SpringBoot2.6.x不兼容问题的处理方案:

如果SpringBoot 2.6.0开始,请求路径与SpringMVC处理映射匹配的默认策略已从AntPathMatcher更改为PathPatternParser。可以通过设置spring.mvc.pathmatch.matching-strategy为ant-path-matcher来改变。

除了basePackage包路径配错以外。以下方案可解决以上问题。

package com.cch.swagger.config;

import org.springframework.beans.BeansException;

import org.springframework.beans.factory.config.BeanPostProcessor;

import org.springframework.util.ReflectionUtils;

import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping;

import springfox.documentation.spring.web.plugins.WebFluxRequestHandlerProvider;

import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider;

import java.lang.reflect.Field;

import java.util.List;

import java.util.stream.Collectors;

/**

* swagger 在 springboot 2.6.x 不兼容问题的处理

* @author cch

* @create 2024-01-25 21:06

*/

public class SwaggerBeanPostProcessor implements BeanPostProcessor {

@Override

public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException

{

if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider)

{

customizeSpringfoxHandlerMappings(getHandlerMappings(bean));

}

return bean;

}

private void customizeSpringfoxHandlerMappings(List mappings)

{

List copy = mappings.stream().filter(mapping -> mapping.getPatternParser() == null)

.collect(Collectors.toList());

mappings.clear();

mappings.addAll(copy);

}

@SuppressWarnings("unchecked")

private List getHandlerMappings(Object bean)

{

try

{

Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings");

field.setAccessible(true);

return (List) field.get(bean);

}

catch (IllegalArgumentException | IllegalAccessException e)

{

throw new IllegalStateException(e);

}

}

}

并更改@Import,添加 SwaggerBeanPostProcessor.class

推荐文章

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。