如果在springboot2.6+中使用springfox swagger即便是最新的3.0版本也会出现NPE(空指针异常) 导致项目启动失败问题。研究其源码发现通过使用自定义一个WebMvcRequestHandlerProvider然后在BeanDefinitionRegistryPostProcessor接口中将已定义的WebMvcRequestHandlerProvider Bean定义替换成步骤三中的即可。只要将步骤一到四中的java文件放到自己项目里就可以在springboot2.6+使用新的path_pattern_parser路径匹配模式而不需要通过兼容配置项(spring.mvc.pathmatch.matching-strategy=ant_path_matcher)指定ant路径匹配模式。
public class WebMvcPatternsRequestConditionWrapper implements springfox.documentation.spring.wrapper.PatternsRequestCondition<PathPatternsRequestCondition> {
private final String contextPath;
private final PathPatternsRequestCondition condition;
public WebMvcPatternsRequestConditionWrapper(String contextPath, PathPatternsRequestCondition condition) {
this.contextPath = contextPath;
this.condition = condition;
}
@Override
public springfox.documentation.spring.wrapper.PatternsRequestCondition combine(
springfox.documentation.spring.wrapper.PatternsRequestCondition<PathPatternsRequestCondition> other) {
if (other instanceof WebMvcPatternsRequestConditionWrapper && !this.equals(other)) {
return new WebMvcPatternsRequestConditionWrapper(
contextPath,
condition.combine(((WebMvcPatternsRequestConditionWrapper) other).condition));
}
return this;
}
@Override
public Set<String> getPatterns() {
return this.condition.getPatterns().stream()
.map(p -> String.format("%s/%s", maybeChompTrailingSlash(contextPath), maybeChompLeadingSlash(p.getPatternString())))
.collect(Collectors.toSet());
}
@Override
public boolean equals(Object o) {
if (o instanceof WebMvcPatternsRequestConditionWrapper) {
return this.condition.equals(((WebMvcPatternsRequestConditionWrapper) o).condition);
}
return false;
}
@Override
public int hashCode() {
return this.condition.hashCode();
}
@Override
public String toString() {
return this.condition.toString();
}
}
public class WebMvcRequestHandler implements RequestHandler {
private final String contextPath;
private final HandlerMethodResolver methodResolver;
private final RequestMappingInfo requestMapping;
private final HandlerMethod handlerMethod;
public WebMvcRequestHandler(
String contextPath,
HandlerMethodResolver methodResolver,
RequestMappingInfo requestMapping,
HandlerMethod handlerMethod) {
this.contextPath = contextPath;
this.methodResolver = methodResolver;
this.requestMapping = requestMapping;
this.handlerMethod = handlerMethod;
}
@Override
public HandlerMethod getHandlerMethod() {
return handlerMethod;
}
@Override
public RequestHandler combine(RequestHandler other) {
return this;
}
@Override
public Class<?> declaringClass() {
return handlerMethod.getBeanType();
}
@Override
public boolean isAnnotatedWith(Class<? extends Annotation> annotation) {
return null != AnnotationUtils.findAnnotation(handlerMethod.getMethod(), annotation);
}
@Override
public PatternsRequestCondition getPatternsCondition() {
return new WebMvcPatternsRequestConditionWrapper(contextPath,requestMapping.getPathPatternsCondition());
}
@Override
public String groupName() {
return ControllerNamingUtils.controllerNameAsGroup(handlerMethod);
}
@Override
public String getName() {
return handlerMethod.getMethod().getName();
}
@Override
public Set<RequestMethod> supportedMethods() {
return requestMapping.getMethodsCondition().getMethods();
}
@Override
public Set<MediaType> produces() {
return requestMapping.getProducesCondition().getProducibleMediaTypes();
}
@Override
public Set<MediaType> consumes() {
return requestMapping.getConsumesCondition().getConsumableMediaTypes();
}
@Override
public Set<NameValueExpression<String>> headers() {
return WebMvcNameValueExpressionWrapper.from(requestMapping.getHeadersCondition().getExpressions());
}
@Override
public Set<NameValueExpression<String>> params() {
return WebMvcNameValueExpressionWrapper.from(requestMapping.getParamsCondition().getExpressions());
}
@Override
public <T extends Annotation> Optional<T> findAnnotation(Class<T> annotation) {
return ofNullable(AnnotationUtils.findAnnotation(handlerMethod.getMethod(), annotation));
}
@Override
public RequestHandlerKey key() {
return new RequestHandlerKey(
requestMapping.getPathPatternsCondition().getDirectPaths(),
requestMapping.getMethodsCondition().getMethods(),
requestMapping.getConsumesCondition().getConsumableMediaTypes(),
requestMapping.getProducesCondition().getProducibleMediaTypes());
}
@Override
public springfox.documentation.spring.wrapper.RequestMappingInfo<?> getRequestMapping() {
return new WebMvcRequestMappingInfoWrapper(requestMapping);
}
@Override
public List<ResolvedMethodParameter> getParameters() {
return methodResolver.methodParameters(handlerMethod);
}
@Override
public ResolvedType getReturnType() {
return methodResolver.methodReturnType(handlerMethod);
}
@Override
public <T extends Annotation> Optional<T> findControllerAnnotation(Class<T> annotation) {
return ofNullable(AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), annotation));
}
@Override
public String toString() {
return new StringJoiner(", ", WebMvcRequestHandler.class.getSimpleName() + "{", "}")
.add("requestMapping=" + requestMapping)
.add("handlerMethod=" + handlerMethod)
.add("key=" + key())
.toString();
}
}
public class WebMvcRequestHandlerProvider implements RequestHandlerProvider {
private final List<RequestMappingInfoHandlerMapping> handlerMappings;
private final HandlerMethodResolver methodResolver;
private final String contextPath;
public WebMvcRequestHandlerProvider(Optional<ServletContext> servletContext,HandlerMethodResolver methodResolver,List<RequestMappingInfoHandlerMapping> handlerMappings) {
this.handlerMappings = handlerMappings;
this.methodResolver = methodResolver;
this.contextPath = servletContext.map(ServletContext::getContextPath).orElse(ROOT);
}
@Override
public List<RequestHandler> requestHandlers() {
return nullToEmptyList(handlerMappings).stream()
.filter(requestMappingInfoHandlerMapping ->
!("org.springframework.integration.http.inbound.IntegrationRequestMappingHandlerMapping"
.equals(requestMappingInfoHandlerMapping.getClass()
.getName())))
.map(toMappingEntries())
.flatMap((entries -> StreamSupport.stream(entries.spliterator(), false)))
.map(toRequestHandler())
.sorted(byPatternsCondition())
.collect(toList());
}
private Function<RequestMappingInfoHandlerMapping,
Iterable<Map.Entry<RequestMappingInfo, HandlerMethod>>> toMappingEntries() {
return input -> input.getHandlerMethods().entrySet();
}
private Function<Map.Entry<RequestMappingInfo, HandlerMethod>, RequestHandler> toRequestHandler() {
return input -> new WebMvcRequestHandler(
contextPath,
methodResolver,
input.getKey(),
input.getValue());
}
}
public class CompatiblePathPatternParser implements BeanDefinitionRegistryPostProcessor, EnvironmentAware {
static final String BEAN_NAME = "webMvcRequestHandlerProvider";
private Environment environment;
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
WebMvcProperties.MatchingStrategy strategy = environment.getProperty("spring.mvc.pathmatch.matching-strategy", WebMvcProperties.MatchingStrategy.class);
if(registry.containsBeanDefinition(BEAN_NAME) && strategy != WebMvcProperties.MatchingStrategy.ANT_PATH_MATCHER){
registry.removeBeanDefinition(BEAN_NAME);
registry.registerBeanDefinition(BEAN_NAME,new RootBeanDefinition(WebMvcRequestHandlerProvider.class));
}
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
}