繁体   English   中英

如何在 Spring 引导中为 Camel 配置 Jackson ObjectMapper

[英]How to configure Jackson ObjectMapper for Camel in Spring Boot

我正在尝试使用 Jackson 在骆驼路线上序列化和反序列化与 JSON 之间的 POJO。 其中一些有 Java 8 LocalDate 字段,我希望它们被序列化为 YYYY-MM-DD 字符串,而不是整数数组。

我们仅将 Java 配置用于我们的 Spring 引导应用程序,因此没有 XML 骆驼配置。

我已经成功地创建了一个 ObjectMapper 来做我想做的事,通过将它添加到我们的依赖项中,我们系统的其他部分正在使用它:

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
</dependency>

这对我们的应用程序配置:

@Bean
public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) {
    return builder
            .featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
            .build();
}

传出 REST 路由示例:

@Component
public class MyRouteBuilder extends RouteBuilder {

    @Override
    public void configure() throws Exception {

        restConfiguration().component("servlet").contextPath("/mycontext")
                .port(8080).bindingMode(RestBindingMode.json);

        rest("/myendpoint)
                .get()
                .route()
                .to("bean:myService?method=myMethod()");
    }
}

传入消息路由示例:

@Component
public class MyRouteBuilder extends RouteBuilder {

    @Autowired
    private MyBean myBean;

    @Override
    public void configure() {
        from(uri)
                .unmarshal().json(JsonLibrary.Jackson)
                .bean(myBean);
    }
}

但是,默认情况下,Camel 创建自己的 ObjectMapper 实例,因此不会使用Jackson2ObjectMapperBuilder自动添加的 JSR310 序列化器/反序列化器或禁用的WRITE_DATES_AS_TIMESTAMPS功能。 我已阅读Camel JSON文档,但它没有显示如何使用 Spring 配置添加自定义数据格式,或如何为所有类型应用全局自定义。

那么如何告诉 Camel 使用我的 ObjectMapper,仅使用 Spring 引导 Java 配置?

我通过单步执行 Camel 代码找到了解决方案。 因此,虽然它可以满足我的要求,但它可能不适用于 Camel 的未来版本,因为它似乎没有记录并且可能不受支持。

我所做的就是将以下 bean 添加到我的 Spring 配置中,以及问题中的ObjectMapper bean:

@Bean(name = "json-jackson")
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public JacksonDataFormat jacksonDataFormat(ObjectMapper objectMapper) {
    return new JacksonDataFormat(objectMapper, HashMap.class);
}

需要注意的关键点:

  • JacksonDataFormat没有采用没有解组类型的ObjectMapper构造函数。 但是,在默认构造函数中,当未提供解组类型时使用HashMap.class ,所以我使用它。 通过某种魔法,这似乎习惯于解组所有 POJO 类型。 如果您还需要其他类的更具体的数据格式,您也需要在其中设置ObjectMapper
  • Camel 似乎会在 bean 注册表中搜索一个名为“json-jackson”的 bean,因此将 Spring bean 设置为使用该名称会使 Camel 不创建新的 bean 而是使用我的。
  • bean 范围必须设置为SCOPE_PROTOTYPE因为 REST DSL 期望获得DataFormat的新实例。 参见CAMEL-7880

在 Java 代码中创建JacksonDataFormat并启用/禁用您想要的功能,然后在 Camel 路由中使用该实例。

 .unmarshal(myInstanceGoesHere).

使用 Spring 和 Camel 2.18.1,我能够通过添加以下依赖项来实现相同的目标:

<dependency>
    <groupId>com.fasterxml.jackson.module</groupId>
    <artifactId>jackson-module-parameter-names</artifactId>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jdk8</artifactId>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
    <version>2.6.1</version>
</dependency>

CamelContextConfiguration类中,自动JacksonDataFormat以配置类路径模块的发现和序列化选项的配置:

@Configuration
public class CamelContextConfig implements CamelContextConfiguration {

    @Autowired
    public JacksonDataFormat jacksonDataFormat;

    @Override
    public void beforeApplicationStart(CamelContext camelContext) {
    }

    @Override
    public void afterApplicationStart(CamelContext camelContext) {
        jacksonDataFormat
            .getObjectMapper()
            .findAndRegisterModules()
            .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    }
}

大家好消息,Spring Boot 现在支持对象映射器自动发现! 只需设置此属性:

camel.dataformat.json-jackson.auto-discover-object-mapper=true

如果设置为 true,那么 Jackson 将在注册表中查找 objectMapper

文档: https : //camel.apache.org/components/latest/dataformats/json-jackson-dataformat.html#_spring_boot_auto_configuration

日志:

INFO o.a.c.impl.engine.AbstractCamelContext   : Apache Camel 3.3.0 (CamelContext: camel-1) is starting
INFO o.a.c.c.jackson.JacksonDataFormat        : Found single ObjectMapper in Registry to use: com.fasterxml.jackson.databind.ObjectMapper@20a1b3ae
WARN o.a.c.c.jackson.JacksonDataFormat        : The objectMapper was already found in the registry, no customizations will be applied

(警告只是表示, camel.dataformat.json-jackson.*下的所有其他属性都被忽略)

到目前为止,只有@david-edwards 的建议对我有用。 我首先定义了一个id为“json-jackson”的数据格式bean

<bean id="json-jackson" class="com.mydomain.JacksonDataFormatExt" />

然后是格式类:

public class JacksonDataFormatExt extends JacksonDataFormat{

    public JacksonDataFormatExt(){
        super();
        setPrettyPrint(true);
        setEnableFeatures(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS.name());
        SimpleModule s = new SimpleModule();
        s.addSerializer(CustomEnum.class, new CustomEnumSerializer());
        addModule(s);
    }
}

和 CustomEnumSerializer 类:

public class CustomEnumSerializer extends JsonSerializer<CustomEnum> {

    @Override
    public void serialize(CustomEnumvalue, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
        String stringValue = value.getNlsText();
        if(stringValue != null && !stringValue.isEmpty() && !stringValue.equals("null")) {
            jgen.writeString(stringValue);
        } else {
            jgen.writeNull();
        }
    }
}

我设法使用org.apache.camel:camel-jackson-starter:2.20.0非常方便地为 Camel 配置了 ObjectMapper

它通过 Spring 应用程序属性公开了一些有用的 ObjectMapper 属性以进行配置。 例如,可以直接从 application.yaml 或 application.properties 文件设置 WRITE_DATES_AS_TIMESTAMPS。

查找 JacksonDataFormatConfiguration 类以获取更多详细信息。

我还需要使用一些 Mixin,所以我仍然需要配置 Camel 以使用 Spring 的 ObjectMapper。 我结束了这个:

配置豆:

@Bean
public Jackson2ObjectMapperBuilderCustomizer customizer() {
    return new Jackson2ObjectMapperBuilderCustomizer() {
        @Override
        public void customize(Jackson2ObjectMapperBuilder builder) {
            builder.mixIn(Person.class, PersonMixin.class);
        }
    }
}

应用程序.yaml:

camel:
  dataformat:
    json-jackson:
      disable-features: WRITE_DATES_AS_TIMESTAMPS
      object-mapper: jacksonObjectMapper

其中jacksonObjectMapper是配置的 Jackson2ObjectMapperBuilder 构建的 ObjectMapper bean 的名称

如果其他人想知道如何使用版本中的修复程序。 2.17 .. 我使用这个 xml 配置让它工作:

 <camel:camelContext id="defaultCamelContext">
       .....
        <camel:dataFormats>
            <camel:json id="json" library="Jackson"  objectMapper="myObjectMapper"/>
        </camel:dataFormats>

 </camel:camelContext>

..其中 myObjectMapper 是 ObjectMapper 类型的 spring bean 的名称

我通过在 pom 中包含 jackson 依赖来解决

<dependency>
  <groupId>org.apache.camel</groupId>
  <artifactId>camel-jackson-starter</artifactId>
  <version>${camel.version}</version>
</dependency>

现在,只需在路由配置中添加 JacksonDataFormat

public void configure() throws Exception {
    JacksonDataFormat jsonDf = new JacksonDataFormat(Card.class);
    jsonDf.setPrettyPrint(true);

    from("direct:endpoint")
    .marshal(jsonDf)
    .convertBodyTo(String.class)
    .....
}

如果 Camel 在那里给你带来麻烦,我会恢复直接使用 bean:

  1. 只需创建一个小型 Json 实用程序,它可以进行编组和解组并将预配置的 ObjectMapper 自动装配到其中。

  2. 利用 Camels 很棒的 Spring bean 集成来调用您的实用程序并转换路由中的消息,例如:

     from(uri) .unmarshal().json(JsonLibrary.Jackson) .beanRef("jsonUtil", "unmarshal") .bean(myBean);

我无法让任何示例工作。 有点失望的是,阅读变通方法非常复杂。

在我看来,camel 应该通过使用应用程序附带的相同 Jackson bean 来轻松使用 Spring 默认对象映射器。

我放弃使用.json()并将其换成处理器。

如下所示,这里使用了 Spring 提供的 objectMapper。

路线

from(CONSUME_TAG)
 .process("jsonProcessor")
 .to("direct:anotherRoute")
 .end();

通用处理器注意这如何自动装配 spring boot objectMapper bean。

@Component
public class JsonProcessor implements Processor {

    @Autowired
    ObjectMapper objectMapper;

    @Override
    public void process(Exchange exchange) throws Exception {
        exchange.getOut().setBody(objectMapper.writeValueAsString(exchange.getIn().getBody()));
    }

}

这对我有用(Camel 2.2.0)

    <dependency>
        <groupId>com.fasterxml.jackson.datatype</groupId>
        <artifactId>jackson-datatype-jsr310</artifactId>
        <version>2.12.5</version>
    </dependency>

休息配置

            restConfiguration().dataFormatProperty("moduleClassNames", "com.fasterxml.jackson.datatype.jsr310.JavaTimeModule")
                               .dataFormatProperty("disableFeatures", "WRITE_DATES_AS_TIMESTAMPS")

Spring Boot 在启动期间使用所需jackson-datatype-jsr310模块配置ObjectMapper ,并将其放入 Spring 上下文。

Camel 的新版本默认不使用ObjectMapper形式 Spring Context 并在没有jackson-datatype-jsr310的情况下创建自己的ObjectMapper

  1. 对于骆驼的新版本,将此添加到application.yaml中。yaml
camel.dataformat.jackson.auto-discover-object-mapper: true
  1. 用于解组
from("some").unmarshal().json(SomeClass.class)

并且不要使用

from("some").unmarshal(new JacksonDataFormat(SomeClass.class))

它不会从 Spring 上下文中获取ObjectMapper

要检查会发生什么调试AbstractJacksonDataFormat.doStart()方法。 它在 Camel 启动期间被调用并从 Spring 上下文中获取ObjectMapper或创建一个新的。 创建新的ObjectMapper时,它不需要 Jackson jackson-datatype-jsr310模块并发生错误。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM