Trouble Shooting

[Spring Boot] Actuator dependency와 Swagger Starter 3.0.0 충돌 에러

꽁치_로그 2023. 4. 14. 15:43

Spring boot 2.7.3에서 Swagger 3.0.0 버전과 함께 Actuator dependency를 추가하는 과정에서 아래와 같은 오류가 발생했다. 

Swagger 3.0.0 dependency를 추가했던 과정에서 발생한 오류와 동일했다.

1. 문제 발생 🤬

Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException
porm.xml - Dependency
1
2
3
4
5
6
7
8
9
10
11
12
13
14
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-boot-starter</artifactId>
            <version>3.0.0</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>3.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
cs

 

2. 문제 해결 😁

Swagger는 모든 endpoint에 대해 documentation을 해주는 기능이다. Actuator 또한 endpoint(refresh, beans, health) 등을 직접 생성하는 역할이다 보니 둘의 의존성이 충돌하게 된다.

Sprinfox는 Sprinf MVC가 Ant-based path matcher를 기본값으로 하여 경로를 찾는 것으로 가정하는데 Spring MVC 2.6.X부터 기본값을 Ant-based path matcher에서 PathPatter-based matcher로 변경하여 발생하는 버그이다.

해결 방법으로는 3가지가 있는데 나는 2번으로 해결하였다.

  1. SpringBoot 버전을 2.5.x이하로 변경한다.
  2. Ant-based path matcher으로 경로 변경 & springfoxHandlerProvider 추가
  3. Springdoc 사용..
application.yml
1
2
3
4
spring:
  mvc:
    pathmatch:
      matching-strategy: ant_path_matcher
cs
SwaggerConfig.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
@Configuration
@EnableSwagger2
public class SwaggerConfig {
 
 
    @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 <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);
                }
            }
        };
    }
 
    // CONTACT
    private static final Contact DEFAULT_CONTACT = new Contact("Kenneth Lee""http://www.joneconsulting.co.kr","edowon@joneconsulting.co.kr" );
 
    // API INFO
    private static final ApiInfo DEFAULT_API_INFO = new ApiInfo("Awesome API Title",
            "My User management REST API Service",
            "1.0",
            "urn:tos",
            DEFAULT_CONTACT,
            "Apache License Version 2.0",
            "http://www.apache.org/licenses/LICENSE-2.0",
            new ArrayList<>());
 
 
    // PRODUCES AND CONSUMES  --> application.yml
    private static final Set<String> DEFAULT_PRODUCES_AND_CONSUMES = new HashSet<>(
            Arrays.asList("application/json""application/xml"));
 
    // localhost:8088/v2/api-docs
    @Bean
    public Docket DocumentApi(){
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(DEFAULT_API_INFO)
                .produces(DEFAULT_PRODUCES_AND_CONSUMES)
                .consumes(DEFAULT_PRODUCES_AND_CONSUMES);
    }
 
}
 
cs

이처럼 config에 springfoxHandlerProvider를 추가하니 문제를 해결했다!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@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 <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);
            }
        }
    };
}
cs

요약 👉 Springboot 2.6.x 이상에서 swagger와 Acutator의 버전 호환성 문제가 발생하면

1) application.yml에  spring: mvc: pathmatch: matching-strategy: ant_path_matcher을 추가하고

2) springfoxHandlerProvider를 추가하자!

 

Reference

https://stackoverflow.com/questions/70589430/nullpointerexception-when-upgrading-spring-boot-to-version-2-6-2

 

NullPointerException when upgrading Spring Boot to version 2.6.2

I upgraded by Spring Boot version from 2.5.4 to 2.6.2. Now my app fails at startup because of this error - see below. Any ideas how to fix it? Error starting ApplicationContext. To display the cond...

stackoverflow.com

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/util/pattern/PathPattern.html

https://middleearth.tistory.com/69

https://dkswnkk.tistory.com/672

 

Spring actuator 의존성 추가만으로 nullpointerexception가 발생할때 해결방법

springboot 2.6.7 springfox 2.9.2 버그 발생 상황 트래픽 모니터링을 위해 actuator와 prometheus 의존성 추가를 하려고 하니 정말 밑도 끝도 없이 caused by: java.lang.nullpointerexception: null이 발생하며 프로젝트가

middleearth.tistory.com

 

반응형