package com.arms.config; import java.lang.reflect.Field; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; import com.fasterxml.classmate.TypeResolver; import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; 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.reactive.result.method.RequestMappingInfoHandlerMapping; import org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerMapping; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.ApiInfo; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.spring.web.plugins.WebFluxRequestHandlerProvider; @Configuration public class Swagger2Config { @Autowired private TypeResolver typeResolver; /** * Spring Boot 2.6+ 와 Springfox 3.0.0 호환성 문제 해결을 위한 BeanPostProcessor * WebFlux 환경용 - Actuator 엔드포인트 처리 시 ClassCastException 방지 */ @Bean public static BeanPostProcessor springfoxHandlerProviderBeanPostProcessor() { return new BeanPostProcessor() { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof WebFluxRequestHandlerProvider) { customizeSpringfoxHandlerMappings(getHandlerMappings(bean)); } return bean; } private void customizeSpringfoxHandlerMappings(List mappings) { List copy = mappings.stream() .filter(mapping -> mapping instanceof RequestMappingHandlerMapping) .collect(Collectors.toList()); mappings.clear(); mappings.addAll(copy); } @SuppressWarnings("unchecked") private List getHandlerMappings(Object bean) { try { Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings"); if(field!=null){ field.setAccessible(true); return (List) field.get(bean); }else{ throw new IllegalStateException("Cannot find handlerMappings field in " + bean.getClass().getName()); } } catch (IllegalArgumentException | IllegalAccessException e) { throw new IllegalStateException(e); } } }; } @Bean public Docket api() { return new Docket(DocumentationType.SWAGGER_2) .useDefaultResponseMessages(false) .consumes(getConsumeContentTypes()) .produces(getProduceContentTypes()) .apiInfo(apiInfo()) .select() .apis(RequestHandlerSelectors.basePackage("com.arms")) .paths(PathSelectors.any()) .build() .genericModelSubstitutes(Mono.class, Flux.class); } private Set getConsumeContentTypes() { Set consumeContentTypes = new HashSet<>(); consumeContentTypes.add("application/json;charset=UTF-8"); consumeContentTypes.add("application/x-www-form-urlencoded"); return consumeContentTypes; } private Set getProduceContentTypes() { Set produceContentTypes = new HashSet<>(); produceContentTypes.add("application/json;charset=UTF-8"); return produceContentTypes; } private ApiInfo apiInfo() { return new ApiInfoBuilder() .title("ARMS API") .description("ARMS API") .build(); } }