如何在 Spring Boot 中为 Camel 配置 Jackson ObjectMapper
Posted
技术标签:
【中文标题】如何在 Spring Boot 中为 Camel 配置 Jackson ObjectMapper【英文标题】:How to configure Jackson ObjectMapper for Camel in Spring Boot 【发布时间】:2016-01-28 14:22:19 【问题描述】:我正在尝试使用 Jackson 对 Camel 路由上的 JSON 序列化和反序列化 POJO。其中一些具有 Java 8 LocalDate 字段,我希望将它们序列化为 YYYY-MM-DD 字符串,而不是整数数组。
我们的 Spring Boot 应用程序只使用 Java 配置,所以没有 XML Camel 配置。
我已经成功地创建了一个 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 配置添加自定义 DataFormat,或如何为所有类型应用全局自定义。
那么我如何告诉 Camel 使用我的 ObjectMapper,只使用 Spring Boot Java 配置?
【问题讨论】:
查看jackson的启用/禁用功能:camel.apache.org/json.html 请分享你的骆驼路线。 @ClausIbsen 我读到了,但它没有显示如何让 Camel 了解自定义 DataFormat。或者如何使它适用于所有 POJO 而不是特定的类。 @FritzDuchardt 我现在已经为问题添加了示例路线。 【参考方案1】:在 java 代码中创建 JacksonDataFormat
并启用/禁用您想要的功能,然后在 Camel 路由中使用该实例。
.unmarshal(myInstanceGoesHere).
【讨论】:
谢谢。这是部分解决方案;我看到了一些不足之处: 1. 使用 restConfiguration() 时,setJsonDataFormat() 需要一个字符串,但我看不到给 JacksonDataFormat 一个名称。每个 rest() 端点都需要添加一个 unmarshal() 吗? 2. 为每条路由添加自定义 JacksonDataFormat 效率低下,可能会被遗忘。我想全局配置一次。 3. 我已经按照我想要的方式配置了一个 ObjectMapper。必须配置一个新的似乎效率低下。这不仅仅是禁用功能;我也需要注册 JSR310 序列化器/反序列化器。 我记录了一张票以允许配置自定义对象映射器实例以用于数据格式:issues.apache.org/jira/browse/CAMEL-9275 谢谢,克劳斯。在我看来,如果有一个现有的 ObjectMapper bean,它应该自动使用,如果不存在,则只创建一个新的 ObjectMapper。 @ClausIbsen 我看到您在 Camel 2.17 中修复了该票证,但我们如何利用该修复程序?尤其是在我们不使用 Spring 的 OSGI 蓝图环境中——我们是否只是通过 ObjectMapper 类型的蓝图 (osgi-config.xml) 创建一个 bean,它会自动获取它? 我尝试在 JsonDataFormat 中使用 setModuleClassNames 启用 JavaTimeModule,但似乎不起作用。不幸的是,这不是启用/禁用功能的问题,所以我看不出这个答案如何解决问题。【参考方案2】:如果 Camel 给你带来麻烦,我会直接使用 bean:
只需创建一个小型 Json 实用程序,该实用程序可以进行编组和解组,并将您的预配置 ObjectMapper 自动装配到其中。
利用 Camels 出色的 Spring bean 集成来调用您的实用程序并在路由中转换消息,例如:
from(uri)
.unmarshal().json(JsonLibrary.Jackson)
.beanRef("jsonUtil", "unmarshal")
.bean(myBean);
【讨论】:
【参考方案3】:我通过单步执行 Camel 代码找到了解决方案。因此,虽然它可以满足我的需求,但它可能不适用于 Camel 的未来版本,因为它似乎没有文档记录并且可能不受支持。
除了问题中的 ObjectMapper
bean 之外,我所做的只是将以下 bean 添加到我的 Spring 配置中:
@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 范围必须设置为SCOPE_PROTOTYPE
,因为 REST DSL 期望获得DataFormat
的新实例。见CAMEL-7880。
【讨论】:
到目前为止,这是唯一对我有用的东西。我的解决方案的一些变化:不需要 objectMapper,我还创建了一个名称为“json-jackson”的 bean,它是 JacksonDataFormat 的扩展。 唯一对我有用的选项(使用 Camel 2.17)!【参考方案4】:使用 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);
【讨论】:
【参考方案5】:到目前为止,只有@david-edwards 的建议对我有用。我首先定义了一个数据格式bean,id为:“json-jackson”
<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();
【讨论】:
【参考方案6】:我设法使用 org.apache.camel:camel-jackson-starter:2.20.0
非常方便地为 Camel 配置 ObjectMapper
它公开了一些有用的 ObjectMapper 属性,用于通过 Spring 应用程序属性进行配置。例如,可以直接从 application.yaml 或 application.properties 文件设置 WRITE_DATES_AS_TIMESTAMPS。
查找 JacksonDataFormatConfiguration 类以获取更多详细信息。
我还需要使用一些 Mixins,所以我仍然需要配置 Camel 以使用 Spring 的 ObjectMapper。我最终得到了这个:
配置bean:
@Bean
public Jackson2ObjectMapperBuilderCustomizer customizer()
return new Jackson2ObjectMapperBuilderCustomizer()
@Override
public void customize(Jackson2ObjectMapperBuilder builder)
builder.mixIn(Person.class, PersonMixin.class);
application.yaml:
camel:
dataformat:
json-jackson:
disable-features: WRITE_DATES_AS_TIMESTAMPS
object-mapper: jacksonObjectMapper
其中jacksonObjectMapper
是配置的Jackson2ObjectMapperBuilder构建的ObjectMapper bean的名称
【讨论】:
【参考方案7】:我无法让任何示例工作。有点失望的是,阅读变通方法非常复杂。
在我看来,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()));
【讨论】:
【参考方案8】:如果其他人想知道如何使用版本中的修复。 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 的名称
【讨论】:
在您包含的骆驼 XML 示例中,您是否也可以包含您的我通过在 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)
.....
【讨论】:
这看起来是目前最好的答案。 JSON Jackson 组件文档还提到,如果您需要自定义ObjectMapper
超出此范围,您可以创建一个 ObjectMapper
bean。 "如果你在注册表中设置了一个 ObjectMapper,那么 Camel 会自动查找并使用这个 ObjectMapper"【参考方案10】:
大家好消息,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.*
下的所有其他属性都将被忽略)
【讨论】:
【参考方案11】:这对我有用 (骆驼2.2.0)
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.12.5</version>
</dependency>
REST 配置
restConfiguration().dataFormatProperty("moduleClassNames", "com.fasterxml.jackson.datatype.jsr310.JavaTimeModule")
.dataFormatProperty("disableFeatures", "WRITE_DATES_AS_TIMESTAMPS")
【讨论】:
以上是关于如何在 Spring Boot 中为 Camel 配置 Jackson ObjectMapper的主要内容,如果未能解决你的问题,请参考以下文章
Camel ActiveMQ + Spring boot 不读取 spring activemq 配置
Apache Camel 与 Spring Boot 集成,通过FTP定时采集处理文件
spring boot + apache camel + mongodb 集成问题
Spring Boot + Apache Camel + Freemarker 自定义模板加载器
在 Spring Boot Camel 应用程序公开的 Micrometer / Prometheus 信息中包含其他 JMX 指标