Подтвердить что ты не робот

Spring Загрузка с двумя конфигурациями MVC

У меня есть приложение загрузки Spring с API REST, используя Jackson для конфигурации представления JSON. Он отлично работает, и я могу получить все ботинки Spring.

Однако мне нужно добавить дополнительный REST API, похожий, но с разными настройками. Например, между прочим, ему нужна другая конфигурация Mapper для объекта Jackson, поскольку JSON будет выглядеть совсем немного (например, без массивов JSON). Это всего лишь один пример, но есть немало различий. Каждый API имеет другой контекст (например,/api/current и /api/legacy ).

В идеале я бы хотел, чтобы две конфигурации MVC отображались в эти разные контексты и не должны были отказываться от какой-либо автоматической проводки вещей при загрузке.

До сих пор все, что мне удалось закрыть, - это использовать два сервлета диспетчера, каждый из которых имеет свою конфигурацию MVC, но это приводит к тому, что Boot выдает целую кучу вещей, которые я получаю автоматически, и в основном побеждает причину использования загрузки.

Я не могу разбить приложение на несколько приложений.

Ответ "вы не можете сделать это с помощью Boot и все еще получаете всю свою магию" - это приемлемый ответ. Похоже, он должен справиться с этим, хотя.

4b9b3361

Ответ 1

Есть несколько способов добиться этого. Основываясь на ваших требованиях, Id говорит, что это случай управления версиями API REST. Существует несколько способов версии REST API, некоторые из которых - это URL-адреса версий и другие методы, упомянутые в ссылках комментариев. Подход, основанный на URL-адресе, более ориентирован на наличие нескольких версий адреса:

Например Для V1:

/path/v1/resource

и V2:

/path/v2/resource

Они разрешат использовать 2 разных метода в Spring MVC Controller bean, которым вызовы будут делегированы.

Другим вариантом решения версий API является использование заголовков, таким образом, существует только URL-адрес, несколько методов, основанных на версии. Например:

/path/resource

ЗАГОЛОВОК:

X-API-Version: 1.0

ЗАГОЛОВОК:

X-API-Version: 2.0

Это также будет разрешено в двух отдельных операциях на контроллере.

Теперь это стратегии, основанные на использовании нескольких версий поколений.

Вышеупомянутые подходы хорошо объясняются в следующем: git example

Примечание. Вышеупомянутое является загрузочным приложением Spring.

Общность в обоих этих подходах заключается в том, что в JSON должны быть разные POJOS, на основе которых библиотека Jackson JSON автоматически сортирует экземпляры указанного типа в JSON.

т.е. Предполагая, что в коде используется @RestController [org.springframework.web.bind.annotation.RestController]

Теперь, если ваше требование состоит в том, чтобы иметь разные конфигурации JSON Mapper, то есть различные конфигурации сопоставления JSON, то независимо от контекстов Spring вам понадобится другая стратегия для сериализации/де-сериализации.

В этом случае вам нужно будет реализовать пользовательский де-сериализатор {CustomDeSerializer}, который расширит JsonDeserializer<T> [com.fasterxml.jackson.databind.JsonDeserializer], а в deserialize() реализует ваш пользовательский старт.

Используйте аннотацию @JsonDeserialize(using = CustomDeSerializer.class) на целевом POJO.

Таким образом, несколько схем JSON могут управляться с помощью разных De-Serializers.

Объединив стратегию останова версий + пользовательскую стратегию сериализации, каждый API можно управлять в своем собственном контексте без необходимости подключения нескольких конфигураций сервлетов диспетчера.

Ответ 2

Развернув мой комментарий вчера и идею @Ashoka Header, я бы предложил зарегистрировать 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;
}

Обратите внимание на пользовательский тип носителя для одного из преобразователей.

Если вам нравится, вы можете использовать Interceptor для перезаписи заголовков версий, предложенных @Ashoka, для пользовательского типа Media, например:

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

Если вы можете жить с другим портом для каждого контекста, вам нужно только перезаписать DispatcherServletAutoConfiguration beans. Все остальное волшебство работает, множество, Джексон и т.д. Вы можете настроить Servlet и Jackson/Multipart и т.д. Для каждого дочернего контекста отдельно и ввести bean родительского контекста.

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, но вы можете установить все остальные свойства spring для дочерних контекстов.

Ответ 4

В Spring -boot ypu может использоваться разные профили (например, dev и test).

Запустите приложение с -Dspring.profiles.active=dev или -Dspring.profiles.active=test и используйте разные файлы свойств с именем application-dev.properties или application-test.properties в вашем каталоге properties. Это может сделать проблему.