You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 08:10:41