【问题标题】:Spring Integration Java DSL — How to configure the JSON transformer to take Spring boot's global settings?Spring Integration Java DSL — 如何配置 JSON 转换器以获取 Spring boot 的全局设置?
【发布时间】:2016-08-13 09:54:19
【问题描述】:

我正在使用集成流来调用 RESTful Web 服务,如下所示:

@Bean
IntegrationFlow flow() throws Exception {
    return IntegrationFlows.from("inputChannel")
            .handle(Http.outboundGateway("http://provider1.com/...")
                    .httpMethod(HttpMethod.GET)
                    .expectedResponseType(ItemDTO[].class))
            .get();
}

事实上,上面的代码完美运行。据我从文档中了解到,Spring 集成的 http outbound-gateway 使用 RestTemplate 的实例将 Http 响应正文转换为 ItemDTOs 的数组。

现在让我们考虑以下代码:

@Bean
IntegrationFlow flow() throws Exception {
    return IntegrationFlows.from("inputChannel")
            .handle(Http.outboundGateway("http://provider2.com/...")
                    .httpMethod(HttpMethod.GET)
                    .expectedResponseType(String.class))
            .<String,String>transform(m -> sirenToHal(m))
            .transform(Transformers.fromJson(ItemDTO[].class))
            .get();
}

在这种情况下,Http响应体被转换为String,然后传递给转换器(例如在我的实际项目中,我使用JOLT将警报文档转换为HAL——JSON资源表示) .然后,我实例化一个转换器来处理 JSON 到 java 对象的映射。令人惊讶的是,上面的代码失败了(例如,在我的项目中,转换器抛出了UnrecognizedPropertyException)。

失败的原因似乎是transformer使用的Object mapper没有和RestTemplate一样配置。我想知道为什么转换器不使用与 RestTemplate 实例相同的 ObjectMapper,或者至少为什么它们不使用相同的配置(即 Spring boot 的全局配置)。无论如何,有没有配置 ObjectMapper 供转换器使用?

更新

我发现了如何配置转换器的对象映射器。

首先,我们创建并配置Jackson的ObjectMapper的一个实例,如下:

ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// + any other additional configuration setting

然后,我们将transformer实例化如下(替换上面代码中的对应行):

.transform(Transformers.fromJson(ItemDTO[].class, new Jackson2JsonObjectMapper(mapper)))

我还是觉得transformer使用的ObjectMapper应该采用Spring boot的全局配置。

【问题讨论】:

    标签: java spring-integration dsl objectmapper


    【解决方案1】:

    这是一个有趣的观点,但是由于您没有将RestTemple 注入Http.outboundGateway(),甚至MappingJackson2HttpMessageConverter,我只看到ObjectToJsonTransformerMappingJackson2HttpMessageConverter 之间的区别,Spring Integration 仅使用@987654330 @,当 Spring Web 在其代码中执行此操作时:

    public MappingJackson2HttpMessageConverter() {
        this(Jackson2ObjectMapperBuilder.json().build());
    }
    

    即使 Spring Boot 自动配置 ObjectMapper bean,我也看不到此硬代码对所有这些功能的任何引用。

    所以,请确认 Jackson2ObjectMapperBuilder.json().build() 也适用于您,并随时提出 JIRA 票,以协调 Spring Integration ObjectMapper 基础架构与 Spring Web。

    更新

    好的。在我看来,我发现了不同之处。我是对的 - Jackson2ObjectMapperBuilder:

    // Any change to this method should be also applied to spring-jms and spring-messaging
    // MappingJackson2MessageConverter default constructors
    private void customizeDefaultFeatures(ObjectMapper objectMapper) {
        if (!this.features.containsKey(MapperFeature.DEFAULT_VIEW_INCLUSION)) {
            configureFeature(objectMapper, MapperFeature.DEFAULT_VIEW_INCLUSION, false);
        }
        if (!this.features.containsKey(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)) {
            configureFeature(objectMapper, DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        }
    }
    

    因此,我们也必须将此选项应用于 Spring Integration。 另见MappingJackson2MessageConverter

    关于此事的 JIRA:https://jira.spring.io/browse/INT-4001

    【讨论】:

    • 再次感谢您对 Artem 的帮助。我还有另一个与我的示例紧密相关的问题:是否可以配置 JSON 转换器来处理集合?在我的示例代码中,我可以将 ParameterizedTypeReference 与 outbound-gateway 组件一起使用,但同样不能与 JSON 转换器一起使用。您可以将此添加为未来版本的改进吗?如果您愿意我为这个问题写一篇新文章,请告诉我。
    • 好吧,ParameterizedTypeReference 是 Spring Web API 的一部分,与 Jackson 完全无关。我们在JsonToObjectTransformer 中有钩子到JsonHeaders 上可能的杰克逊类型技巧。请参阅docs.spring.io/spring-integration/reference/html/… 中的JSON Transformers 部分。对于集合类型,您必须在请求 Message 中使用 JsonHeaders.TYPE_IDJsonHeaders.CONTENT_TYPE_ID 标头。但是从另一端而不是集合,您可以指定数组类型,例如type="com.my.proj.tPerson[]".
    • 杰克逊的TypeReference怎么样?我的意思是,如果我们有类似的东西会很棒:Transformers.fromJson(new TypeReference&lt;List&lt;ItemDTO&gt;() {})。我认为这会简化消息组的聚合,例如我的示例here
    • 是的,我明白你的意思。但是 Spring Integration 转换器不受目标 JSON 引擎实现的影响。我们使用我们自己的JsonObjectMapper 抽象。正如我所说:随时提出 JIRA,我们将考虑如何处理 ParameterizedTypeReference。当它出现在 SI 核心中时,我们可能会在 DSLTransformers.fromJson() factory 中添加这样的技巧。