我有一个带有REST API的Spring Boot应用程序,使用Jackson进行JSON视图配置。它工作得很好,我可以获得Spring Boot的所有优点。
但是,我需要添加一个类似但具有不同设置的其他REST API。例如,除其他外,它需要一个不同的Jackson对象映射器配置,因为JSON看起来会有点不同(例如没有JSON数组)。这只是一个例子,但有很多不同之处。每个API都有不同的上下文(例如/ api / current和/ api / legacy)。
理想情况下,我希望两个MVC配置映射到这些不同的上下文,而不必放弃启动时任何自动连接的东西。
到目前为止,所有我能够接近的是使用两个调度程序servlet,每个都有自己的MVC配置,但这导致Boot丢弃了我自动得到的一大堆内容,基本上无法使用它的原因引导。
我无法将应用分解为多个应用。
答案"你不能用Boot做到这一点并仍然得到它的所有魔力"是一个可接受的答案。看起来它应该能够解决这个问题。
答案 0 :(得分:4)
有几种方法可以实现这一目标。根据您的要求,Id说这是管理REST API版本的情况。 有几种方法可以对REST API进行版本控制,其中一些是流行的版本URL,以及评论链接中提到的其他技术。 基于URL的方法更倾向于拥有多个版本的地址:
例如 对于V1 :
/path/v1/resource
和V2 :
/path/v2/resource
这些将解析Spring MVC Controller bean中的两个不同方法,调用将被委托给它们。
解析API版本的另一个选项是使用标头,这样只有URL,基于版本的多种方法。 例如:
/path/resource
标题强>
X-API-Version: 1.0
标题强>
X-API-Version: 2.0
这也将在控制器上的两个单独操作中解决。
现在这些是可以处理多个休息版本的策略。
以下解释了上述方法:git example
注意:以上是春季启动应用程序。
这两种方法的共同点是,需要有不同的POJOS,基于哪个Jackson JSON库自动将指定类型的实例编组为JSON。
即。假设代码使用@RestController [org.springframework.web.bind.annotation.RestController]
现在,如果您的要求是使用不同的JSON Mapper,即不同的JSON映射器配置,那么无论Spring上下文如何,您都需要一个不同的序列化/反序列化策略。
在这种情况下,您需要实现一个自定义反序列化器{CustomDeSerializer},它将扩展JsonDeserializer<T>
[com.fasterxml.jackson.databind.JsonDeserializer]
并在deserialize()
中实现您的自定义策略。
在目标POJO上使用@JsonDeserialize(using = CustomDeSerializer.class)
注释。
这样可以使用不同的De-Serializers管理多个JSON方案。
通过结合静态版本控制+自定义序列化策略,每个API都可以在自己的上下文中进行管理,而无需连接多个调度程序Servlet配置。
答案 1 :(得分:4)
扩展我对昨天的评论和@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提出的版本标题重写为自定义媒体类型,如下所示:
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");
}
.....
}
}
这可能不是您正在寻找的确切答案,但也许它可以提供一些灵感。拦截器已注册like so。
答案 2 :(得分:2)
如果您可以为每个上下文使用不同的端口,那么您只需要覆盖DispatcherServletAutoConfiguration
bean。所有其余的魔法作品,multpart,Jackson等。您可以分别为每个子上下文配置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属性。
答案 3 :(得分:-3)
在Spring-boot中,ypu可以使用不同的配置文件(例如dev
和test
)。
启动应用程序
-Dspring.profiles.active=dev
或-Dspring.profiles.active=test
并在application-dev.properties
目录中使用名为application-test.properties
或properties
的不同属性文件。
这可能会解决问题。