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

升级Corretto-Java17+Tomcat10.1后遇CXF资源类指定异常

问题:升级至Corretto-Java17、Tomcat10.1.x、Spring6、CXF4后部署报错

已完成javax到jakarta的包迁移,部署Tomcat时抛出:
jakarta.servlet.ServletException: At least one resource class should be specified

异常栈信息

at org.apache.cxf.jaxrs.servlet.CXFNonSpringJaxrsServlet.getServiceClasses(CXFNonSpringJaxrsServlet.java:304)
at org.apache.cxf.jaxrs.servlet.CXFNonSpringJaxrsServlet.init(CXFNonSpringJaxrsServlet.java:153)
at org.apache.catalina.core.StandardWrapper.initServlet(StandardWrapper.java:944)
at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:901)
at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:805)
at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:4601)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:4894)

web.xml配置

<?xml version="1.0" encoding="UTF-8"?>
<web-app  xmlns="https://jakarta.ee/xml/ns/jakartaee" 
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee 
 https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
 version="5.0">
 
    <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath*:papi-plugin.xml,classpath*:papi-server.xml,classpath*:applicationContext.xml</param-value>
        </context-param>
    <listener>
 <listener-class> 
  org.springframework.web.context.ContextLoaderListener
 </listener-class>
        </listener>
    
        <servlet>
            <servlet-name>CXFServlet</servlet-name>
            <servlet-class>
org.apache.cxf.jaxrs.servlet.CXFNonSpringJaxrsServlet
</servlet-class>
            <init-param>
                <param-name>jakarta.ws.rs.core.Application</param-name>
                <param-value>com.resource.ProvisionCxfApp</param-value>
            </init-param>
            <load-on-startup>1</load-on-startup>
        </servlet>
    
        <servlet-mapping>
            <servlet-name>CXFServlet</servlet-name>
            <url-pattern>/prv/v1/*</url-pattern>
        </servlet-mapping>
    
    <servlet>
            <servlet-name>abc</servlet-name>
            <servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
            <load-on-startup>1</load-on-startup>
        </servlet>
    
        <servlet-mapping>
            <servlet-name>abc</servlet-name>
            <url-pattern>/servlet/*</url-pattern>
        </servlet-mapping>

</web-app>         

ProvisionCxfApp类代码

import jakarta.servlet.ServletException;
import jakarta.ws.rs.core.Application;
import java.util.HashSet;
import java.util.Set;

public class ProvisionCxfApp extends Application {

 private final Set<Object> singletons = new HashSet<Object>();
    /**
     * @return
     * @see jakarta.ws.rs.core.Application#getClasses()
     */
    @Override
    public Set<Class<?>> getClasses() {
        Set<Class<?>> set = new HashSet<Class<?>>();
        set.add(AResource.class);
        set.add(AcsResource.class);
        return set;
    }

    public ProvisionCxfApp() throws ServletException{       
        singletons.add(AResource.class);
        singletons.add(AcsResource.class);
 }}

资源类代码

@Path("/ac/{id}")
public class AResource extends BResource {}


@Path("/acs")
public class AcsResource extends BResource {}

解决方案

问题出在ProvisionCxfApp的构造函数里——你把类对象添加到了singletons集合中,但singletons应该存放的是实例对象,而非Class类型。CXF初始化时会检查getSingletons()返回的集合,发现里面是Class而非实例后会忽略这些条目,进而影响对getClasses()返回结果的识别,最终抛出"至少需要指定一个资源类"的错误。

修改步骤:

  1. 重写getSingletons()方法,返回正确的实例集合
  2. 修正构造函数,添加实例对象而非Class对象

修改后的ProvisionCxfApp代码:

import jakarta.servlet.ServletException;
import jakarta.ws.rs.core.Application;
import java.util.HashSet;
import java.util.Set;

public class ProvisionCxfApp extends Application {

 private final Set<Object> singletons = new HashSet<Object>();

    public ProvisionCxfApp() throws ServletException{       
        // 添加实例对象,而非Class
        singletons.add(new AResource());
        singletons.add(new AcsResource());
    }

    @Override
    public Set<Class<?>> getClasses() {
        Set<Class<?>> set = new HashSet<Class<?>>();
        set.add(AResource.class);
        set.add(AcsResource.class);
        return set;
    }

    @Override
    public Set<Object> getSingletons() {
        return singletons;
    }
}

额外检查项:

  • 确认AResource和AcsResource类上的@Path注解使用的是jakarta.ws.rs.Path(而非旧的javax包)
  • 确保所有CXF相关依赖都是4.x版本,没有混合旧版本的javax包依赖
  • 检查Tomcat的lib目录中没有遗留的javax.servlet或javax.ws.rs相关jar包,避免类加载冲突

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 16:33:18