๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
BackEnd๐ŸŒฑ/Spring

Actuator dependency and Swagger Stater 3.0.0 ์ถฉ๋Œ ์—๋Ÿฌ

by ์•ˆ์ฃผํ˜• 2022. 11. 11.

์ƒํ™ฉ

ํ˜„์žฌ Spring Boot 2.7.4 ๋ฒ„์ „์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๊ณ , Swagger 3.0.0 ๋ฒ„์ „์„ ์ด์šฉํ•˜๊ณ  ์žˆ๋Š” ๋„์ค‘ Actuator dependency๋ฅผ ์ถ”๊ฐ€ํ•˜์˜€๋”๋‹ˆ ์•„๋ž˜์™€ ๊ฐ™์€ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด์„œ ์‹คํ–‰๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.

๋”๋ณด๊ธฐ

Error

org.springframework.context.ApplicationContextException: Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException
	at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:181) ~[spring-context-5.3.23.jar:5.3.23]
	at org.springframework.context.support.DefaultLifecycleProcessor.access$200(DefaultLifecycleProcessor.java:54) ~[spring-context-5.3.23.jar:5.3.23]
	at org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup.start(DefaultLifecycleProcessor.java:356) ~[spring-context-5.3.23.jar:5.3.23]
	at java.base/java.lang.Iterable.forEach(Iterable.java:75) ~[na:na]
	at org.springframework.context.support.DefaultLifecycleProcessor.startBeans(DefaultLifecycleProcessor.java:155) ~[spring-context-5.3.23.jar:5.3.23]
	at org.springframework.context.support.DefaultLifecycleProcessor.onRefresh(DefaultLifecycleProcessor.java:123) ~[spring-context-5.3.23.jar:5.3.23]
	at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:935) ~[spring-context-5.3.23.jar:5.3.23]
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:586) ~[spring-context-5.3.23.jar:5.3.23]
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:147) ~[spring-boot-2.7.4.jar:2.7.4]
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:734) ~[spring-boot-2.7.4.jar:2.7.4]
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:408) ~[spring-boot-2.7.4.jar:2.7.4]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:308) ~[spring-boot-2.7.4.jar:2.7.4]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1306) ~[spring-boot-2.7.4.jar:2.7.4]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1295) ~[spring-boot-2.7.4.jar:2.7.4]
	at com.example.userservice.UserServiceApplication.main(UserServiceApplication.java:16) ~[classes/:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
	at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
	at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49) ~[spring-boot-devtools-2.7.4.jar:2.7.4]
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 java.base/java.util.Comparator.lambda$comparing$77a9974f$1(Comparator.java:469) ~[na:na]
	at java.base/java.util.TimSort.countRunAndMakeAscending(TimSort.java:355) ~[na:na]
	at java.base/java.util.TimSort.sort(TimSort.java:220) ~[na:na]
	at java.base/java.util.Arrays.sort(Arrays.java:1515) ~[na:na]
	at java.base/java.util.ArrayList.sort(ArrayList.java:1750) ~[na:na]
	at java.base/java.util.stream.SortedOps$RefSortingSink.end(SortedOps.java:392) ~[na:na]
	at java.base/java.util.stream.Sink$ChainedReference.end(Sink.java:258) ~[na:na]
	at java.base/java.util.stream.Sink$ChainedReference.end(Sink.java:258) ~[na:na]
	at java.base/java.util.stream.Sink$ChainedReference.end(Sink.java:258) ~[na:na]
	at java.base/java.util.stream.Sink$ChainedReference.end(Sink.java:258) ~[na:na]
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:485) ~[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.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.23.jar:5.3.23]
	... 19 common frames omitted

 

ํ•ด๋‹น ์˜ค๋ฅ˜๋Š” ์ด์ „์— Swagger Starter 3.0.0์„ ์ด์šฉํ•  ๋•Œ ํ•œ๋ฒˆ ๊ฒช์—ˆ๋˜ ๋ฌธ์ œ์˜€๋Š”๋ฐ ์•„๋ž˜์™€ ๊ฐ™์€ ๋ฐฉ๋ฒ•์œผ๋กœ ํ•ด๊ฒฐํ•œ ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

 

[Spring] nested exception is java.lang.NullPointerException

์„œ๋ก  spring boot 2.6.7์„ ์‚ฌ์šฉ ์ค‘์ด๊ณ  swagger 3.0.0 ๋ฒ„์ „์„ ์ด์šฉํ•˜๊ธฐ ์œ„ํ•ด ์ง„ํ–‰ํ•˜๋˜ ์ค‘ ์•„๋ž˜์™€ ๊ฐ™์€ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค. Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerExcep

dkswnkk.tistory.com

ํ•˜์ง€๋งŒ ์œ„์™€ ๊ฐ™์€ ๋ฐฉ๋ฒ•์œผ๋กœ ํ•ด๊ฒฐํ–ˆ์Œ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  Actuator dependency๋ฅผ ์ถ”๊ฐ€ํ•˜์ž ๋™์ผํ•œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค. 

 

์›์ธ

์ผ๋‹จ Swagger๋Š” ๋ชจ๋“  endpoint์— ๋Œ€ํ•ด์„œ documentation์„ ํ•ด์ฃผ๋Š” ์—ญํ• ์ž…๋‹ˆ๋‹ค. Actuator ๋˜ํ•œ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ๋ช‡๋ช‡ endpoint(refresh, beans, health)๋“ฑ์„ ์ง์ ‘ ์ƒ์„ฑํ•˜๋Š” ์—ญํ• ์ด๋‹ค ๋ณด๋‹ˆ ์˜์กด์„ฑ์ด ์ถฉ๋Œํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

Springfox๋Š” Spring MVC๊ฐ€ Ant-based path matcher๋ฅผ ๊ธฐ๋ณธ ๊ฐ’์œผ๋กœ ํ•˜์—ฌ ๊ฒฝ๋กœ๋ฅผ ์ฐพ๋Š” ๊ฒƒ์œผ๋กœ ๊ฐ€์ •ํ•˜๋Š”๋ฐ, Spring MVC๊ฐ€ 2.6.X ๋ฒ„์ „ ์ด์ƒ๋ถ€ํ„ฐ ๊ธฐ๋ณธ๊ฐ’์„ Ant-based path matcher์—์„œ PathPattern-based matcher๋กœ ๋ณ€๊ฒฝํ•˜์—ฌ์„œ ๋ฐœ์ƒํ•˜๋Š” ๋ฒ„๊ทธ์ž…๋‹ˆ๋‹ค.

์ฐธ๊ณ : https://middleearth.tistory.com/69

 

ํ•ด๊ฒฐ

์•„๋ž˜์˜ ๋นˆ์„ ๋“ฑ๋กํ•ด์ค€๋‹ค๋ฉด ํ•ด๊ฒฐ๋ฉ๋‹ˆ๋‹ค.

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

 

์˜ˆ์‹œ

@Configuration
public class Config {
    @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);
                }
            }
        };
    }

 

์ฐธ๊ณ 

 

How to befriend Spring Boot 2.6.X with Actuator dependency and Swagger Starter 3.0.0

I saw this issue: Springboot 2.6.0 / Spring fox 3 - Failed to start bean 'documentationPluginsBootstrapper' Top answer say to put configuration property like that spring.mvc.pathmatch.match...

stackoverflow.com

 

๋Œ“๊ธ€