복붙노트

[SPRING] 두 개의 MVC 구성으로 봄 부팅

SPRING

두 개의 MVC 구성으로 봄 부팅

JSON 뷰 구성을 위해 Jackson을 사용하여 REST API가있는 Spring Boot 응용 프로그램이 있습니다. 그것은 훌륭하게 작동하고 나는 모든 Spring Boot 장점을 얻을 수 있습니다.

그러나 비슷하지만 다른 설정을 사용하는 추가 REST API를 추가해야합니다. 예를 들어, JSON은 상당히 다르게 보일 것이므로 (예 : JSON 배열 없음) 다른 Jackson 개체 매퍼 구성이 필요합니다. 그것은 하나의 예일 뿐이지 만 몇 가지 차이점이 있습니다. 각 API는 다른 문맥을 가지고 있습니다 (예 : / api / current 및 / api / legacy).

이상적으로 두 개의 MVC configs가 서로 다른 컨텍스트에 매핑되고 부팅시 자동 배선을 포기할 필요가 없습니다.

지금까지 내가 접근 할 수 있었던 것은 각각 자체 MVC 구성을 가진 두 개의 디스패처 서블릿을 사용하는 것이었지만 부트로 인해서 자동으로 기본적으로 부트 사용 이유를 무효화하는 결과가 발생했습니다.

앱을 여러 개의 앱으로 나눌 수 없습니다.

대답은 "당신은 부팅으로 이것을 할 수없고 여전히 모든 마법을 얻습니다"라는 대답이 수용 가능한 대답입니다. 그래도 이걸 처리 할 수 ​​있어야합니다.

해결법

  1. ==============================

    1.이를 달성하는 데는 여러 가지 방법이 있습니다. 요구 사항에 따라 ID는 REST API 버전을 관리하는 경우라고합니다. REST API의 버전을 올리는 방법은 여러 가지가 있습니다. 인기있는 버전 중 일부는 버전 URL이며 주석 링크에서 언급 된 다른 기술입니다. URL 기반 접근 방식은 주소의 여러 버전을 사용하는쪽으로 더욱 집중됩니다.

    이를 달성하는 데는 여러 가지 방법이 있습니다. 요구 사항에 따라 ID는 REST API 버전을 관리하는 경우라고합니다. REST API의 버전을 올리는 방법은 여러 가지가 있습니다. 인기있는 버전 중 일부는 버전 URL이며 주석 링크에서 언급 된 다른 기술입니다. URL 기반 접근 방식은 주소의 여러 버전을 사용하는쪽으로 더욱 집중됩니다.

    예를 들어 V1의 경우 :

    /path/v1/resource
    

    및 V2 :

    /path/v2/resource
    

    이것들은 호출이 위임되는 Spring MVC Controller 빈에서 두 가지 메소드로 해결 될 것이다.

    API의 버전을 해결할 수있는 또 다른 옵션은 머리글을 사용하는 것입니다.이 방법은 URL 만 있고 버전을 기반으로하는 여러 가지 방법이 있습니다. 예 :

    /path/resource
    

    머리글:

    X-API-Version: 1.0
    

    머리글:

    X-API-Version: 2.0
    

    또한 컨트롤러에서 두 개의 개별 작업으로 해결됩니다.

    이제는 여러 개의 나머지 버전을 처리 할 수있는 전략을 기반으로합니다.

    위의 접근법은 다음에서 잘 설명됩니다 : git example

    참고 : 위의 내용은 스프링 부트 응용 프로그램입니다.

    이 두 방법의 공통점은 Jackson JSON 라이브러리가 지정된 유형의 인스턴스를 JSON으로 자동 마샬링하는 데 필요한 POJOS가 다를 필요가 있다는 것입니다.

    나. 코드가 @RestController [org.springframework.web.bind.annotation.RestController]를 사용한다고 가정하면,

    이제 서로 다른 JSON 매퍼, 즉 다른 JSON 매퍼 구성이 필요한 경우 스프링 컨텍스트에 관계없이 직렬화 / 비 직렬화에 대해 다른 전략이 필요합니다.

    이 경우 JsonDeserializer [com.fasterxml.jackson.databind.JsonDeserializer]를 확장하고 deserialize ()에서 사용자 정의 시작을 구현할 사용자 정의 부 직렬화 기 {CustomDeSerializer}를 구현해야합니다.

    대상 POJO에 @JsonDeserialize (using = CustomDeSerializer.class) 주석을 사용하십시오.

    이 방식으로 여러 개의 JSON 구성표를 다른 디시리얼라이저로 관리 할 수 ​​있습니다.

    나머지 버전 관리 + 맞춤형 직렬화 전략을 결합하여 각 API는 여러 Dispatcher 서블릿 구성을 연결하지 않고도 자체 컨텍스트에서 관리 할 수 ​​있습니다.

  2. ==============================

    2.어제와 @Ashoka 헤더 아이디어에 대한 내 의견을 확대하여 커스텀 미디어 유형에 대해 2 개의 MessageConverters (레거시 및 현재)를 등록 할 것을 제안합니다. 다음과 같이 할 수 있습니다.

    어제와 @Ashoka 헤더 아이디어에 대한 내 의견을 확대하여 커스텀 미디어 유형에 대해 2 개의 MessageConverters (레거시 및 현재)를 등록 할 것을 제안합니다. 다음과 같이 할 수 있습니다.

    @Bean
    MappingJackson2HttpMessageConverter currentMappingJackson2HttpMessageConverter() {
        MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
        ObjectMapper objectMapper = new ObjectMapper();
        // set features
        jsonConverter.setObjectMapper(objectMapper);
    
        jsonConverter.setSupportedMediaTypes(Arrays.asList(new MediaType("json", "v2")));
    
        return jsonConverter;
    }
    
    
    @Bean
    MappingJackson2HttpMessageConverter legacyMappingJackson2HttpMessageConverter() {
        MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
        ObjectMapper objectMapper = new ObjectMapper();
        // set features
        jsonConverter.setObjectMapper(objectMapper);
        return jsonConverter;
    }
    

    변환기 중 하나의 사용자 정의 용지 유형에주의하십시오.

    원한다면 요격기를 사용하여 @Ashoka가 제안한 버전 헤더를 다음과 같이 사용자 정의 Media-Type으로 다시 작성할 수 있습니다.

    public class ApiVersionMediaTypeMappingInterceptor extends HandlerInterceptorAdapter {
        @Override
        public boolean preHandle(HttpServletRequest request,
                                 HttpServletResponse response, Object handler) throws Exception {
            try {
                if(request.getHeader("X-API-Version") == "2") {
                    request.setAttribute("Accept:","json/v2");
                }
           .....
        }
    }
    

    이것은 당신이 찾고 있던 정확한 답변이 아닐지 모르지만 어쩌면 영감을 줄 수 있습니다. 인터셉터는 그렇게 등록됩니다.

  3. ==============================

    3.각 컨텍스트마다 다른 포트로 살 수 있다면 DispatcherServletAutoConfiguration 빈을 겹쳐 써야합니다. 나머지 모든 마법 작업, 멀티 파트, 잭슨 등. 각 하위 컨텍스트에 대해 서블릿 및 Jackson / Multipart 등을 개별적으로 구성하고 상위 컨텍스트의 빈을 주입 할 수 있습니다.

    각 컨텍스트마다 다른 포트로 살 수 있다면 DispatcherServletAutoConfiguration 빈을 겹쳐 써야합니다. 나머지 모든 마법 작업, 멀티 파트, 잭슨 등. 각 하위 컨텍스트에 대해 서블릿 및 Jackson / Multipart 등을 개별적으로 구성하고 상위 컨텍스트의 빈을 주입 할 수 있습니다.

    package test;
    
    import static org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_BEAN_NAME;
    import static org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME;
    
    import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
    import org.springframework.boot.builder.SpringApplicationBuilder;
    import org.springframework.boot.context.embedded.ServletRegistrationBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.PropertySource;
    import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
    import org.springframework.web.servlet.DispatcherServlet;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
    
    @Configuration
    @EnableAutoConfiguration(exclude = {
            Application.Context1.class,
            Application.Context2.class
    })
    public class Application extends WebMvcConfigurerAdapter {
    
        @Bean
        public TestBean testBean() {
            return new TestBean();
        }
    
        public static void main(String[] args) {
            final SpringApplicationBuilder builder = new SpringApplicationBuilder().parent(Application.class);
            builder.child(Context1.class).run();
            builder.child(Context2.class).run();
        }
    
        public static class TestBean {
        }
    
        @Configuration
        @EnableAutoConfiguration(exclude = {Application.class, Context2.class})
        @PropertySource("classpath:context1.properties")
        public static class Context1 {
    
            @Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
            DispatcherServlet dispatcherServlet() {
                DispatcherServlet dispatcherServlet = new DispatcherServlet();
                // custom config here
                return dispatcherServlet;
            }
    
            @Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
            ServletRegistrationBean dispatcherServletRegistration() {
                ServletRegistrationBean registration = new ServletRegistrationBean(dispatcherServlet(), "/test1");
                registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
                // custom config here
                return registration;
            }
    
            @Bean
            Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder(TestBean testBean) {
                System.out.println(testBean);
                Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
                // custom config here
                return builder;
            }
        }
    
        @Configuration
        @EnableAutoConfiguration(exclude = {Application.class, Context1.class})
        @PropertySource("classpath:context2.properties")
        public static class Context2 {
    
            @Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
            DispatcherServlet dispatcherServlet() {
                DispatcherServlet dispatcherServlet = new DispatcherServlet();
                // custom config here
                return dispatcherServlet;
            }
    
            @Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
            ServletRegistrationBean dispatcherServletRegistration() {
                ServletRegistrationBean registration = new ServletRegistrationBean(dispatcherServlet(), "/test2");
                registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
                // custom config here
                return registration;
            }
    
            @Bean
            Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder(TestBean testBean) {
                System.out.println(testBean);
                Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
                // custom config here
                return builder;
            }
        }
    }
    

    context1 / 2.properties 파일에는 현재 server.port = 8080 / 8081 만 포함되어 있지만 하위 컨텍스트에 대한 다른 모든 스프링 속성을 설정할 수 있습니다.

  4. ==============================

    4.스프링 부트에서는 다른 프로파일 (예 : dev 및 test)을 사용할 수 있습니다.

    스프링 부트에서는 다른 프로파일 (예 : dev 및 test)을 사용할 수 있습니다.

    다음 프로그램으로 애플리케이션 시작 -Dspring.profiles.active = dev 또는 -Dspring.profiles.active = test 특성 디렉토리 내의 application-dev.properties 또는 application-test.properties라는 다른 특성 파일을 사용하십시오. 그게 문제가 될 수 있습니다.

  5. from https://stackoverflow.com/questions/34728814/spring-boot-with-two-mvc-configurations by cc-by-sa and MIT license