升级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()返回结果的识别,最终抛出"至少需要指定一个资源类"的错误。
修改步骤:
- 重写
getSingletons()方法,返回正确的实例集合 - 修正构造函数,添加实例对象而非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
相关产品推荐
相关产品推荐

