SpringbootV2.6整合Knife4j 3.0.3 问题记录

参考

https://juejin.cn/post/7249173717749940284

近期由于升级到springboot2.6X,所以服务端很多组件都需要重新导入以及解决依赖问题。
下面就是一个很经典的问题了,
springboot2.6与knife4j的整合。

版本对应

springboot2.6与knife4j 3.0.3

  • 两者路径匹配模式不同

由于Springfox使用的路径匹配是基于AntPathMatcher,而Spring Boot 2.6.X使用的是PathPatternMatcher,所以将MVC的路径匹配规则改成 AntPathMatcher。具体配置如下:

--- application.yml:
spring:
  mvc:
    pathmatch:
      #Springfox使用的路径匹配是基于AntPathMatcher的,而Spring Boot 2.6.X使用的是PathPatternMatcher 解决knife4j匹配boot2.6.7 boot版本过高的问题
      matching-strategy: ant_path_matcher
      
-- 或者:application.properties:
spring.mvc.pathmatch.matching-strategy=ANT_PATH_MATCHER
  • springboot加载springfox过程中失败:
    第一个点很多文章都提到了,但是我主要遇到的是第二个点,表现在:
"org.springframework.web.servlet.mvc.condition.PatternsRequestCondition.getPatterns()" because "this.condition" is null

这时候你可以

新增一个配置类,注意配置类不能放到swagger的配置类下,测试无效。配置Bean可以放在启动类下或者重新新增一个配置Bean,代码如下:

package net.w2p.AppBase.fixBugs;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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;

/**
 * Springboot整合Knife4j问题修正
 */
@Configuration
public class BeanPostProcessorConfig {

    @Bean
    public 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);
                }
            }
        };
    }

}


  • 配置demo
package net.w2p.AppBase;


import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import io.swagger.annotations.ApiOperation;
import net.w2p.WebExt.config.AppENV;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import springfox.documentation.RequestHandler;
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.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import javax.servlet.http.HttpSession;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import static springfox.documentation.builders.PathSelectors.regex;


/*
 * @Api: 修饰整个类,描述Controller的作用
 * @ApiOperation: 描述一个类的一个方法,或者说一个接口
 * @ApiParam: 单个参数描述
 * @ApiModel: 用对象来接收参数
 * @ApiProperty: 用对象接收参数时,描述对象的一个字段
 * @ApiResponse: HTTP响应其中1个描述
 * @ApiResponses: HTTP响应整体描述
 * @ApiIgnore: 使用该注解忽略这个API
 * @ApiError :发生错误返回的信息
 * @ApiImplicitParam: 一个请求参数
 * @ApiImplicitParams: 多个请求参数
 *  doc.html
 */

@EnableSwagger2
@Configuration
@EnableKnife4j

public class Swagger2Configuration implements WebMvcConfigurer {

    private String accessToken="token";

    private String title;

    private String description;

    private String version;

    private String termsOfServiceUrl;

    private String name;

    private String url;

    private String email;


    @Bean
    public Docket createRestApi() {
        boolean isEnableSwagger=true;
        if(!"test".equalsIgnoreCase(AppENV.getEnv())&&!"dev".equalsIgnoreCase(AppENV.getEnv())){
            isEnableSwagger=false;
        }
        if("dev".equalsIgnoreCase(AppENV.getEnv())){
            isEnableSwagger=true;
        }else{
            isEnableSwagger=false;
        }
        String scanPaths = "net.w2p.AppBase.controller.api;" +
                "net.w2p.AppBase.controller.common;" ;

        return new Docket(DocumentationType.SWAGGER_2).enable(isEnableSwagger)
                .apiInfo(apiInfo())
                //                .enable(enableSwagger)
                .directModelSubstitute(Timestamp.class, Long.class)//将Timestamp类型全部转为Long类型
                .directModelSubstitute(Date.class, Long.class)//将Date类型全部转为Long类型
                .forCodeGeneration(true)
                .select()

                .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
//                .apis(RequestHandlerSelectors.any())
                .apis(basePackage(scanPaths))
//                .paths(PathSelectors.ant("/api/**"))
//                .paths(regex("^.*(?<!error)$"))
                .paths(PathSelectors.any())
                .build()
                // 添加忽略类型
                .ignoredParameterTypes(HttpSession.class)
                .securitySchemes(securitySchemes())
                .securityContexts(securityContexts());
    }

    private List<SecurityScheme> securitySchemes() {
        List<SecurityScheme> apiKeyList= new ArrayList();
        apiKeyList.add(new ApiKey("token", "token", "header"));
        return apiKeyList;
    }

    private List<SecurityContext> securityContexts() {
        List<SecurityContext> securityContexts=new ArrayList<>();
        securityContexts.add(
                SecurityContext.builder()
                        .securityReferences(defaultAuth())
                        .forPaths(regex("^(?!auth).*$"))
                        .build());
        return securityContexts;
    }


    private List<SecurityReference> defaultAuth() {
        AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
        AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
        authorizationScopes[0] = authorizationScope;
        List<SecurityReference> securityReferences = new ArrayList<>();
        securityReferences.add(new SecurityReference(accessToken, authorizationScopes));
        return securityReferences;
    }


    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("api文档")
                .description("综合前台网站")
                .termsOfServiceUrl("http://www.baidu.com")
                .contact(new Contact("freeLife","baidu.com","1000@qq.com"))
                .version("2.9.2")
                .build();
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        System.out.println("================> add doc.html -url");
        registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");
//        registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
    }

    // 定义分隔符
    private static final String splitor = ";";
    /**
     * 声明基础包
     *
     * @param basePackage 基础包路径
     * @return
     */
    public static Predicate<RequestHandler> basePackage(final String basePackage) {
        return input -> declaringClass(input).transform(handlerPackage(basePackage)).or(true);
    }

    /**
     * 校验基础包
     *
     * @param basePackage 基础包路径
     * @return
     */
    private static Function<Class<?>, Boolean> handlerPackage(final String basePackage) {
        return input -> {
            for (String strPackage : basePackage.split(splitor)) {
                boolean isMatch = input.getPackage().getName().startsWith(strPackage);
                if (isMatch) {
                    return true;
                }
            }
            return false;
        };
    }

    /**
     * 检验基础包实例
     *
     * @param requestHandler 请求处理类
     * @return
     */
    @SuppressWarnings("deprecation")
    private static Optional<? extends Class<?>> declaringClass(RequestHandler requestHandler) {
        return Optional.fromNullable(requestHandler.declaringClass());
    }
}

运行结果

在这里插入图片描述
正常运行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值