[Spring] Springfox PathPattern 호환 이슈

양정훈·2022년 6월 17일

이슈

  • Spring 5.3 M1 (Spring Boot 2.4) 부터 지원하는 PathPattern을 사용하면 Springfox가 호환되지 않는 이슈가 있다.
  • Spring Boot 2.6 부터는 설정 기본값도 PathPattern으로 바뀌었음

해결

  • Spring Boot 2.6, Springfox 3.0.0 에서의 해결 방법이며, 다른 버전에서도 유효한지는 모르겠음

  • Spring Boot 2.6 에서만 가능한 해결 방법이라기 보다는 2.6에서 PathPattern이 기본 값이 되어서 많은 사람들이 이 이슈를 문의했고, 해서 관련 내용들이 대부분 2.6 기준으로 나와있는게 아닌가 싶음

  • application.yml

# ant path matcher로 변경
spring.mvc.pathmatch.matching-strategy: ant-path-matcher
  • SpringFoxConfiguration.java
@Configuration
public class SpringFoxConfiguration {
	@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);
                }
            }
        };
    }    
}

코드 분석 (작성중)

  • 구동시 아래와 같은 에러가 발생한다.
org.springframework.context.ApplicationContextException: Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException
... 생략

Caused by: java.lang.NullPointerException: null
	at springfox.documentation.spring.web.WebMvcPatternsRequestConditionWrapper.getPatterns(WebMvcPatternsRequestConditionWrapper.java:56) ~[springfox-spring-webmvc-3.0.0.jar:3.0.0]
	at springfox.documentation.RequestHandler.sortedPaths(RequestHandler.java:113) ~[springfox-core-3.0.0.jar:3.0.0]
	at springfox.documentation.spi.service.contexts.Orderings.lambda$byPatternsCondition$3(Orderings.java:89) ~[springfox-spi-3.0.0.jar:3.0.0]
... 생략

	at springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider.requestHandlers(WebMvcRequestHandlerProvider.java:81) ~[springfox-spring-webmvc-3.0.0.jar:3.0.0]
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195) ~[na:na]
	at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1655) ~[na:na]
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484) ~[na:na]
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474) ~[na:na]
	at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913) ~[na:na]
	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[na:na]
	at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578) ~[na:na]
	at springfox.documentation.spring.web.plugins.AbstractDocumentationPluginsBootstrapper.withDefaults(AbstractDocumentationPluginsBootstrapper.java:107) ~[springfox-spring-web-3.0.0.jar:3.0.0]
	at springfox.documentation.spring.web.plugins.AbstractDocumentationPluginsBootstrapper.buildContext(AbstractDocumentationPluginsBootstrapper.java:91) ~[springfox-spring-web-3.0.0.jar:3.0.0]
	at springfox.documentation.spring.web.plugins.AbstractDocumentationPluginsBootstrapper.bootstrapDocumentationPlugins(AbstractDocumentationPluginsBootstrapper.java:82) ~[springfox-spring-web-3.0.0.jar:3.0.0]
	at springfox.documentation.spring.web.plugins.DocumentationPluginsBootstrapper.start(DocumentationPluginsBootstrapper.java:100) ~[springfox-spring-web-3.0.0.jar:3.0.0]
	at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:178) ~[spring-context-5.3.19.jar:5.3.19]
	... 19 common frames omitted
  • springframework.DefaultLifecycleProcessor.doStart()
    -> springfox.DocumentationPluginsBootstrapper.start()
    -> springfox.AbstractDocumentationPluginsBootstrapper.bootstrapDocumentationPlugins()
    -> springfox.AbstractDocumentationPluginsBootstrapper.buildContext()
    -> springfox.AbstractDocumentationPluginsBootstrapper.withDefaults()
    -> springfox.WebMvcRequestHandlerProvider.requestHandlers()
    -> springfox.Orderings.byPatternsCondition()
    -> springfox.WebMvcRequestHandlerProvider.requestHandlers()
    -> springfox.WebMvcPatternsRequestConditionWrapper.getPatterns()
    의 순서로 호출됨
  • 마지막 getPatterns() 에서 아래와 같이
    springframework.PatternsRequestCondition.getPatterns() 를 호출하는데,
    여기서 NPE가 발생. this.conditionnull 이다.
public class WebMvcPatternsRequestConditionWrapper {
	@Override
    public Set<String> getPatterns() {
		return this.condition.getPatterns()
		...
	}
}

결론

  • Springdoc을 쓰자!!! 링크

참고

0개의 댓글