测试Camel路由时Jackson序列化LocalDate报InvalidDefinitionException问题
搞定Apache Camel Jackson Marshal处理LocalDate时的InvalidDefinitionException
问题根源
- 依赖范围错了:你把所有核心依赖都设成
compileOnly,这玩意儿只在编译阶段有用,运行时根本不会把这些依赖打包进去,Jackson的JSR310模块自然找不到。 - 自定义ObjectMapper没被Camel用上:测试里new的ObjectMapper只是个局部变量,Camel根本不知道它存在,还是在用自己默认的配置。
- 配置属性没生效:
camel.dataformat.jackson.auto-discover-object-mapper=true得确保Spring容器里有个配置好的ObjectMapper Bean,不然白设。
一步步解决
1. 先把依赖改对
把compileOnly换成implementation,这样运行时才能加载到需要的类:
configurations { testImplementation.extendsFrom implementation } buildscript { ext { springBootVersion = '3.0.2' camelVersion = '4.0.0-M1' } } dependencies{ implementation group: 'org.springframework.boot', name: 'spring-boot-starter', version: springBootVersion implementation group: 'org.apache.camel.springboot', name: 'camel-spring-boot-starter', version: camelVersion implementation group: 'org.apache.camel.springboot', name: 'camel-jackson-starter', version: camelVersion implementation group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-jsr310', version: '2.14.2' testImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-test', version: springBootVersion testImplementation group: 'org.apache.camel', name: 'camel-test-spring-junit5', version: camelVersion testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' }
注:Spring Boot 3.x和Jackson 2.14+版本匹配,Camel 4.0-M1也能和Spring Boot 3.0.2兼容,放心用。
2. 注册全局的ObjectMapper
写个配置类,把带JavaTimeModule的ObjectMapper放进Spring容器,让Camel能自动找到:
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class JacksonConfig { @Bean public ObjectMapper objectMapper() { ObjectMapper objectMapper = new ObjectMapper(); objectMapper.registerModule(new JavaTimeModule()); // 要是想把LocalDate序列化成"2023-03-27"这种字符串,而不是数组,加下面这行 // objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); return objectMapper; } }
3. 让Camel的配置生效
在application.properties里加一行:
camel.dataformat.jackson.auto-discover-object-mapper=true
这行的作用是让Camel不用自己造ObjectMapper,直接用Spring容器里的那个。
4. 把测试代码里的bug改了
测试里变量名写错了(ex和exchange混着用),而且不用手动new ObjectMapper,直接用容器里的就行:
@SpringBootTest @CamelSpringBootTest @EnableRouteCoverage public class MyRouteTest { @Autowired private CamelContext camelContext; @Autowired private ProducerTemplate producerTemplate; @Test public void testMyRoute() throws Exception { Exchange exchange = new DefaultExchange(camelContext); Map<String, Object> map = new LinkedHashMap<>(); map.put("name", "Rick"); map.put("birth-date", LocalDate.of(2023, 3, 27)); exchange.getMessage().setBody(map); Exchange result = producerTemplate.send("direct:MyRoute", exchange); // 举个断言例子,检查日期是否正确序列化 String jsonBody = result.getMessage().getBody(String.class); assertThat(jsonBody).contains("\"birth-date\":\"2023-03-27\""); } }
5. 备选方案:直接在路由里指定ObjectMapper
要是不想搞全局配置,也可以在路由里直接用自定义的ObjectMapper:
@Component public class MyRoute extends RouteBuilder { @Autowired private ObjectMapper objectMapper; @Override public void configure() throws Exception { JacksonDataFormat jacksonDataFormat = new JacksonDataFormat(); jacksonDataFormat.setObjectMapper(objectMapper); from("direct:MyRoute") .routeId("MyRoute") .marshal(jacksonDataFormat); } }
验证一下
- 先跑
clean build确保依赖都加载对了 - 检查运行时类路径里有没有
jackson-datatype-jsr310-2.14.2.jar - 看Camel的日志,有没有用到自定义ObjectMapper的记录
内容的提问来源于stack exchange,提问作者scafrs
相关产品推荐
相关产品推荐

