在微服务中引入Knife4j,我使用的是将Knife4j放到公共的配置模块下,这样其他微服务需要使用到swagger就可以直接导入使用,在配置文件配置我需要的一些配置即可,这样的优势在于可以完美兼容所有的微服务,并且不需要去重复配置。从此告别手写开发文档
PS:如果是单应用的话也可以使用
代码部分
引入依赖,最新版即可
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
新建Knife4jConfiguration类
使用到EnableSwagger2注解,用来开启swagger和EnableKnife4j来开启knife4j
/**
* @program: e-commerce
* @description: knife4j配置类
* @author: JiaChaoYang
* @date: 2022-09-24 21:03
**/
@Configuration
@EnableSwagger2
@EnableKnife4j
public class Knife4jConfiguration {
/*需要在配置文件里配置这三个配置*/
/*配置开启禁用swagger*/
@Value("${swagger.enabled}")
private boolean enabled;
/*配置模块名*/
@Value("${swagger.groupName}")
private String groupName;
/*配置需要扫描的包*/
@Value("${swagger.basePackage}")
private String basePackage;
/**
* @Description: Swagger 实例 Bean是Docket,所以通过配置Docket实例来配置Swagger
* @return: springfox.documentation.spring.web.plugins.Docket
* @Author: JiaChaoYang
* @Date: 2022-09-24 - 11:36
*/
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.OAS_30)//文档类型,我这里使用的是swagger3
//是否启用swagger
.enable(enabled)
//包名,模块名
.groupName(groupName)
//删除swagger中的操作的响应体
.useDefaultResponseMessages(false)
//展示在Swagger页面上的自定义工程描述信息
.apiInfo(apiInfo())
//选择展示哪些接口
.select()
//配置需要扫描的路径(controller)
.apis(RequestHandlerSelectors.basePackage(basePackage))
//给所有文档都生成文档路径
.paths(PathSelectors.any())
.build();
}
/**
* @Description: Swagger的描述信息
* @return: springfox.documentation.service.ApiInfo
* @Author: JiaChaoYang
* @Date: 2022-09-24 - 11:33
*/
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
//描述信息
.description("测试Knife4j")
//可以通过swagger联系一个人,即联系方式
.contact(new Contact("JiaChaoYang", "https://127.0.0.1:9001/doc.html", "j15030047216@163.com"))
//版本
.version("v1.0")
//标题
.title("测试文档")
.build();
}
为了防止不在同一包下扫描不到需要在resources下新建META-INF文件夹,再在META-INF文件夹下新建spring.factories(推荐),或者启动类使用@ComponentScan扫描(推荐)。或者把包名全部改为一样的(不推荐)
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.qihengyun.core.Knife4jConfiguration
其他模块引用,只需要在bootstrap.yml或者application.yml中配置这三个参数就可以了
1.是否开启Swagger。在生产环境下使用的话会暴露接口,所以在配置文件去控制是否打开
2.模块名
3.需要扫描的类的路径
swagger:
enabled: true
groupName: 登录鉴权微服务模块
basePackage: 你的controller路径
需要在拦截器配置让MVC加载Swagger的静态资源
/**
* @Description: 让MVC加载Swagger的静态资源
* @Param: [registry]
* @return: void
* @Author: JiaChaoYang
* @Date: 2022/9/24 - 20:55
*/
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").
addResourceLocations("classpath:/static/");
registry.addResourceHandler("swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("doc.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
super.addResourceHandlers(registry);
}
如果有拦截器和过滤器的话需要将Swagger放到白名单,即不去鉴权,需要把下边的配置给排除掉
"springfox",
"swagger",
"v3",
"webjars",
"doc.html",
"favicon.ico",
"api-docs"
运行(http://127.0.0.1:端口/doc.html)
到这里配置就结束了,当然还有一个很严重的问题,就是springboot2.6和springfox不兼容的问题,在配置文件去修改mvc匹配策略有点治标不治本,所以参考了一下GitHub上的大佬的配置,只需要放到Knife4j的配置类即可
@Bean
public static BeanPostProcessor springfoxHandlerProviderBeanPostProcessor() {
return new BeanPostProcessor() {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) {
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);
}
}
};
}