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

Apache CXF创建动态客户端时遭遇ClassCastException问题求助

Apache CXF动态客户端JAXB类加载冲突问题解决

问题背景

基于Spring Boot 2.7 + JDK11构建的应用,使用Apache CXF创建动态WebService客户端,读取WSDL并解析为Java对象。JDK11已移除JAXB库,通过Gradle间接引入了jakarta.xml.bind-api-2.3.3.jar、jaxb-impl-2.3.0.1.jar、jaxb-core-2.3.0.1.jar依赖。

初始异常

使用默认方式创建客户端:

this.wsdlClient = dcf.createClient(this.serviceURL + "?wsdl");

抛出编译异常:

javax.xml.bind.JAXBException: "report.balance" doesnt contain ObjectFactory.class or jaxb.index

自定义类加载器后的异常

尝试指定URLClassLoader加载外部applibs目录下的JAR:

this.wsdlClient = dcf.createClient(this.serviceURL + "?wsdl", new URLClassLoader(new URL[] {
     new File("applibs\\jakarta.xml.bind-api-2.3.3.jar").toURI().toURL(),
     new File("applibs\\jaxb-impl-2.3.0.1.jar").toURI().toURL(),
     new File("applibs\\jaxb-core-2.3.0.1.jar").toURI().toURL(),
     new File("applibs\\javax.activation-api-1.2.0.jar").toURI().toURL()
}));

出现类转换异常:

javax.xml.bind.JAXBException: Provider class com.sun.xml.bind.v2.ContextFactory could not be instantiated: javax.xml.bind.JAXBException: ClassCastException: attempting to cast jar:file:/C:/app/applibs/jakarta.xml.bind-api-2.3.3.jar!/javax/xml/bind/JAXBContext.class to jar:file:/C:/app/build/libs/app.jar!/BOOT-INF/lib/jakarta.xml.bind-api-2.3.3.jar!/javax/xml/bind/JAXBContext.class.  Please make sure that you are specifying the proper ClassLoader.

问题根源

  1. 初始编译异常:CXF动态编译WSDL生成Java类时,JAXB相关类未被纳入编译类路径,导致无法生成JAXB必需的ObjectFactory等类。
  2. 类转换异常:自定义URLClassLoader遵循双亲委派机制,会先委托Spring Boot应用类加载器加载JAXBContext接口,而应用JAR中已存在一份jakarta.xml.bind-api,导致自定义类加载器加载的JAXB实现类,与应用类加载器加载的接口属于不同类实例(类的唯一性由加载器+全类名共同决定),触发类型转换错误。

解决方案

方案1:复用应用类加载器并配置编译类路径(推荐)

无需额外复制JAR,直接让CXF使用应用类加载器,同时确保编译时能访问JAXB依赖:

DynamicClientFactory dcf = DynamicClientFactory.newInstance();
// 设置使用当前线程的应用类加载器
dcf.setClassLoader(Thread.currentThread().getContextClassLoader());
// 将应用类路径传入CXF编译器,确保JAXB依赖在编译时可见
JavaCompiler compiler = dcf.getJavaCompiler();
compiler.getOptions().addClasspath(System.getProperty("java.class.path"));
this.wsdlClient = dcf.createClient(this.serviceURL + "?wsdl");

方案2:自定义类加载器修改双亲委派逻辑

如果必须使用外部JAR,重写URLClassLoader优先加载JAXB相关类:

class CustomJaxbClassLoader extends URLClassLoader {
    public CustomJaxbClassLoader(URL[] urls, ClassLoader parent) {
        super(urls, parent);
    }

    @Override
    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
        // 优先加载JAXB相关类,避免委托父类加载器引入重复类
        if (name.startsWith("javax.xml.bind.") 
            || name.startsWith("com.sun.xml.bind.")
            || name.startsWith("javax.activation.")) {
            synchronized (getClassLoadingLock(name)) {
                Class<?> loadedClass = findLoadedClass(name);
                if (loadedClass == null) {
                    try {
                        loadedClass = findClass(name);
                    } catch (ClassNotFoundException e) {
                        // 本地找不到再委托父类加载
                        loadedClass = super.loadClass(name, resolve);
                    }
                }
                if (resolve) {
                    resolveClass(loadedClass);
                }
                return loadedClass;
            }
        }
        // 其他类遵循双亲委派机制
        return super.loadClass(name, resolve);
    }
}

使用该类加载器创建客户端:

ClassLoader appClassLoader = Thread.currentThread().getContextClassLoader();
CustomJaxbClassLoader customClassLoader = new CustomJaxbClassLoader(new URL[] {
     new File("applibs\\jakarta.xml.bind-api-2.3.3.jar").toURI().toURL(),
     new File("applibs\\jaxb-impl-2.3.0.1.jar").toURI().toURL(),
     new File("applibs\\jaxb-core-2.3.0.1.jar").toURI().toURL(),
     new File("applibs\\javax.activation-api-1.2.0.jar").toURI().toURL()
}, appClassLoader);
this.wsdlClient = dcf.createClient(this.serviceURL + "?wsdl", customClassLoader);

方案3:统一JAXB依赖版本

通过Gradle确保JAXB API与实现版本匹配,避免依赖冲突:

dependencies {
    implementation 'jakarta.xml.bind:jakarta.xml.bind-api:2.3.3'
    implementation 'com.sun.xml.bind:jaxb-impl:2.3.7' // 与API版本兼容的最新实现
    implementation 'com.sun.xml.bind:jaxb-core:2.3.0.1'
    implementation 'javax.activation:javax.activation-api:1.2.0'
    // CXF Spring Boot starter
    implementation 'org.apache.cxf:cxf-spring-boot-starter-jaxws:3.5.5'
}

配置完成后直接使用默认方式创建客户端即可。

关键注意事项

  • JDK11中JAXB为可选依赖,必须显式引入,且需保证API与实现版本兼容。
  • Spring Boot嵌入式Tomcat使用自定义类加载器LaunchedURLClassLoader,直接创建URLClassLoader时需处理双亲委派逻辑,避免类重复加载。
  • CXF动态编译WSDL时,需确保JAXB相关类在编译类路径中,而非仅运行时类路径。

内容的提问来源于stack exchange,提问作者gburgalum01

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 22:18:29