Spring Boot 3(Java17)中JAXB XmlAdapter被忽略问题求助
解决Spring Boot 3中JAXB @XmlJavaTypeAdapter被忽略的问题
核心原因
Spring Boot 3完全迁移到Jakarta EE规范,原javax.xml.bind包下的注解已被弃用,必须使用jakarta.xml.bind包下的对应注解;同时Java 17移除了内置JAXB实现,需手动引入运行时依赖。另外若项目中存在Jackson XML依赖,Spring可能优先用它处理XML,导致JAXB适配器不生效。
分步解决方案
1. 确保依赖正确(Maven/Gradle)
添加Jakarta JAXB API和运行时实现:
Maven pom.xml
<dependencies> <!-- Jakarta JAXB API --> <dependency> <groupId>jakarta.xml.bind</groupId> <artifactId>jakarta.xml.bind-api</artifactId> </dependency> <!-- JAXB Runtime 实现 --> <dependency> <groupId>org.glassfish.jaxb</groupId> <artifactId>jaxb-runtime</artifactId> <scope>runtime</scope> </dependency> <!-- Spring Boot Web 依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies>
Gradle build.gradle
dependencies { implementation 'jakarta.xml.bind:jakarta.xml.bind-api' runtimeOnly 'org.glassfish.jaxb:jaxb-runtime' implementation 'org.springframework.boot:spring-boot-starter-web' }
2. 替换所有JAXB注解为Jakarta版本
检查TestClass和适配器类的注解导入,确保使用jakarta.xml.bind.annotation.*和jakarta.xml.bind.annotation.adapters.*包下的类:
修正后的TestClass:
import jakarta.xml.bind.annotation.XmlAccessorType; import jakarta.xml.bind.annotation.XmlAccessType; import jakarta.xml.bind.annotation.XmlElement; import jakarta.xml.bind.annotation.XmlRootElement; import jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapter; import java.io.Serializable; @XmlRootElement @XmlAccessorType(XmlAccessType.FIELD) public class TestClass implements Serializable { @XmlJavaTypeAdapter(MonetaryAmountAdapter.class) @XmlElement(name = "taxTotal") private MonetaryAmount taxTotal; // 省略getter/setter方法 }
修正后的MonetaryAmountAdapter:
import jakarta.xml.bind.annotation.adapters.XmlAdapter; import javax.money.MonetaryAmount; import java.math.BigDecimal; public class MonetaryAmountAdapter extends XmlAdapter<String, MonetaryAmount> { @Override public MonetaryAmount unmarshal(String value) throws Exception { String[] split = value.split("_"); if(split.length==2) return MoneyUtil.monetaryAmount(new BigDecimal(split[0]), split[1]); return MoneyUtil.monetaryAmount(new BigDecimal(split[0]), "USD"); } @Override public String marshal(MonetaryAmount value) throws Exception { if (!value.getCurrency().getCurrencyCode().equals("USD")) return value.getNumber().toString() + "_" + value.getCurrency().getCurrencyCode(); else return value.getNumber().toString(); } }
3. 强制Spring使用JAXB作为XML转换器
若项目引入了Jackson XML依赖,Spring会优先用它处理XML,需配置JAXB转换器为优先级最高:
创建配置类:
import jakarta.xml.bind.JAXBContext; import jakarta.xml.bind.JAXBException; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import java.util.List; @Configuration public class XmlConfig implements WebMvcConfigurer { @Bean public Jaxb2RootElementHttpMessageConverter jaxbMessageConverter() throws JAXBException { Jaxb2RootElementHttpMessageConverter converter = new Jaxb2RootElementHttpMessageConverter(); // 手动指定绑定类,确保适配器被加载 JAXBContext jaxbContext = JAXBContext.newInstance(TestClass.class); converter.setJaxbContext(jaxbContext); return converter; } @Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { // 将JAXB转换器放在最前面,优先使用 converters.add(0, jaxbMessageConverter()); } }
4. 验证接口返回格式
确保控制器方法明确指定返回XML格式:
import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class TestController { @GetMapping(value = "/test", produces = MediaType.APPLICATION_XML_VALUE) public TestClass getTestData() { TestClass test = new TestClass(); // 设置非USD货币的测试值 test.setTaxTotal(MoneyUtil.monetaryAmount(new BigDecimal("100.00"), "EUR")); return test; } }
额外排查点
- 若
MonetaryAmount是第三方类(如JSR 354的javax.money.MonetaryAmount),确保适配器能正确处理其序列化逻辑; - 不要给适配器添加
@Component或@Configurable注解,JAXB会自行实例化适配器,无需Spring容器管理。
内容的提问来源于stack exchange,提问作者Nitin
相关产品推荐
相关产品推荐

