
WildFly 26 升级后,@JsonValue 和 @JsonSerialize 等 Jackson 注解失效,导致 JSON 输出不符合预期(如包装类返回对象而非数组、日期格式化丢失),根本原因在于类路径冲突:自定义安全模块引入了高版本 Jackson JAR,覆盖了 WildFly 内置的 Jackson 2.x 提供器。
wildfly 26 升级后,`@jsonvalue` 和 `@jsonserialize` 等 jackson 注解失效,导致 json 输出不符合预期(如包装类返回对象而非数组、日期格式化丢失),根本原因在于类路径冲突:自定义安全模块引入了高版本 jackson jar,覆盖了 wildfly 内置的 jackson 2.x 提供器。
在 WildFly 26(基于 Jakarta EE 9+ 和 RESTEasy 4.7+)中,JSON 序列化默认由 RESTEasy 的 Jackson 2 集成模块(resteasy-jackson2-provider)驱动,它深度绑定 WildFly 的模块系统与类加载器策略。当应用或自定义模块(如本例中的认证模块)显式打包或声明了独立的 Jackson 依赖(如 jackson-databind-2.13.4.2.jar),会引发以下关键问题:
- ✅ 类加载优先级冲突:WildFly 的模块类加载器(ModuleClassLoader)按依赖声明顺序解析类。若自定义模块将 Jackson JAR 声明为 resources 或 jar,其 com.fasterxml.jackson.* 类将优先于 org.jboss.resteasy.resteasy-jackson2-provider 模块中的同名类被加载;
- ❌ 注解处理器失效:@JsonValue、@JsonSerialize 等注解由 Jackson 的 ObjectMapper 解析执行。一旦 ObjectMapper 实例来自外部 JAR(而非 RESTEasy 托管的实例),其配置(如模块注册、序列化器查找逻辑)将脱离 WildFly 容器管控,导致自定义序列化器(如 JacksonListSerializer)不被调用;
- ❌ @JsonFormat 被忽略:日期格式化依赖 SimpleDateFormat 注册及 JavaTimeModule(或旧版 JodaModule)支持,而外部 Jackson 版本可能未启用对应模块,或与 RESTEasy 的 MessageBodyWriter 集成断裂。
正确解决方案:统一使用 WildFly 内置 Jackson 提供器
1. 移除冗余 Jackson JAR
检查所有自定义模块(尤其是 module.xml 文件),删除
<!-- ❌ 错误:显式引入 Jackson,破坏模块隔离 --> <resources><resource-root path="jackson-core-2.13.4.jar"></resource-root><resource-root path="jackson-databind-2.13.4.2.jar"></resource-root><resource-root path="jackson-annotations-2.13.4.jar"></resource-root></resources>
2. 声明标准 RESTEasy Jackson 依赖
在对应模块的 module.xml 中,仅声明 WildFly 官方提供的依赖:
<!-- ✅ 正确:委托给容器管理的 Jackson 生态 --> <dependencies><module name="org.jboss.resteasy.resteasy-jaxrs"></module><module name="org.jboss.resteasy.resteasy-jackson2-provider"></module></dependencies>
3. 强制 RESTEasy 优先使用 Jackson(可选但推荐)
在 standalone.conf(Linux)或 standalone.conf.bat(Windows)中添加 JVM 参数:
-D"resteasy.preferJacksonOverJsonB"="true"
此参数确保当存在多个 JSON 提供器(如 resteasy-json-binding-provider)时,RESTEasy 明确选择 Jackson 2 作为默认序列化引擎。
4. Maven 依赖调整(关键)
将 pom.xml 中 Jackson 依赖的
<dependency><groupid>com.fasterxml.jackson.core</groupid><artifactid>jackson-databind</artifactid><version>2.13.4.2</version><!-- ✅ 改为 compile,确保应用层可见,但需配合模块系统避免重复 --></dependency><!-- 同时在 resteasy-client 或其他依赖中排除旧版 Jackson --><exclusions><exclusion><groupid>com.fasterxml.jackson.core</groupid><artifactid>jackson-databind</artifactid></exclusion></exclusions>
⚠️ 注意:WildFly 26 自带 Jackson 2.13.x,因此应用 WAR 中无需再打包 Jackson JAR。
provided 是正确选择,但必须确保无其他模块重复提供——这正是本例的根本矛盾点。
5. 验证与调试技巧
启动 WildFly 时添加 -Djboss.modules.system.pkgs=org.jboss.resteasy,com.fasterxml.jackson,便于排查类加载来源;
-
在 MainResource 中临时注入 ObjectMapper,打印其哈希值与类加载器:
@Context private Providers providers; @GET public Response debug() { ObjectMapper mapper = providers.getProvider(JsonMapper.class).get(); System.out.println("Mapper CL: " + mapper.getClass().getClassLoader()); System.out.println("Mapper hash: " + mapper); return Response.ok().build(); }若输出显示 ModuleClassLoader(含 resteasy-jackson2-provider 字样),则集成成功。
总结
WildFly 26 的 Jackson 行为变化并非 Bug,而是模块化设计的必然结果:容器要求生态组件统一受管,拒绝“自带轮子”式的依赖嵌入。解决此类问题的核心原则是——
? 信任容器:优先使用 org.jboss.resteasy.resteasy-jackson2-provider 模块;
? 清理污染:彻底移除任何手动引入的 Jackson JAR;
? 明确契约:通过 resteasy.preferJacksonOverJsonB 和 jboss-deployment-structure.xml 显式声明意图。
完成上述步骤后,@JsonValue 将正确触发 ResponseListWrapper.getObjects() 返回数组,@JsonFormat 日期也将按指定 pattern 格式化,所有自定义序列化逻辑回归预期轨道。










