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

迁移至Jersey 3.1.x时资源注册行为不确定问题求助

问题描述

我们正从Tomcat 9.x/Jersey 2.x迁移到Tomcat 10.1.x/Jersey 3.1.x,部署WAR包时遇到资源类注册的不确定行为:

  • WAR包内容:
    • 标注@Provider或@Path的各类资源类
    • 自定义Application extends ResourceConfig子类,通过register()注册所需的部分资源,该类标注@ApplicationPath("/"),web.xml中无Servlet映射
  • 异常表现:部署/重新部署后首次请求@Path端点时,约50%的情况getClasses()仅返回手动注册的资源子集(符合预期);另外50%返回WAR包内所有资源类(不符合预期)
  • 根因分析:符合预期场景下,应用的configure()方法在资源类实例化前执行;不符合预期场景下,configure()在部分@Provider标注的过滤器初始化后执行,getClasses()调用时机不确定导致注册行为异常,且后续请求保持该状态

根据Jersey 3.1.3文档说明:

若自定义Application子类打包在WAR中,它将定义要使用的资源。若getClasses()和getSingletons()均返回空集合,Jersey会自动扫描WAR包发现所有根资源类和提供者;若任一方法返回非空集合,则仅发布这些类或单例。

这种不确定行为导致认证过滤器等关键资源无法正常工作,需要消除该异常,考虑禁用默认资源自动发现模式(保留@ApplicationPath和@WebListener扫描)。

解决方案

1. 明确禁用Jersey自动扫描机制

在自定义的ResourceConfig子类中,通过设置Jersey内置属性强制关闭自动扫描,确保仅使用手动注册的资源:

public class CustomApplication extends ResourceConfig {
    public CustomApplication() {
        // 禁用自动发现所有资源和Provider
        property(CommonProperties.FEATURE_AUTO_DISCOVERY_DISABLE, true);
        // 保留Jersey核心特性,避免禁用必要内置功能
        property(CommonProperties.FEATURE_AUTO_DISCOVERY_DISABLE_EXCEPTIONS, 
                 Arrays.asList("org.glassfish.jersey.server.ServerProperties", 
                               "org.glassfish.jersey.servlet.ServletProperties"));
        
        // 手动注册所需的资源和Provider
        register(AuthFilter.class);
        register(UserResource.class);
        // ... 其他需要注册的类
    }
}

2. 覆盖getClasses()和getSingletons()方法,确保返回明确集合

避免依赖configure()的执行时机,直接覆盖这两个方法返回手动注册的集合,彻底切断自动扫描的触发条件:

public class CustomApplication extends ResourceConfig {
    private final Set<Class<?>> registeredClasses = new HashSet<>();
    
    public CustomApplication() {
        // 手动注册资源并记录到集合
        register(AuthFilter.class);
        registeredClasses.add(AuthFilter.class);
        
        register(UserResource.class);
        registeredClasses.add(UserResource.class);
        // ... 其他注册
    }

    @Override
    public Set<Class<?>> getClasses() {
        return Collections.unmodifiableSet(registeredClasses);
    }

    @Override
    public Set<Object> getSingletons() {
        return Collections.emptySet(); // 若无单例资源则返回空
    }
}

3. 显式在web.xml中配置Jersey Servlet

放弃@ApplicationPath的自动注册,改为在web.xml中显式配置Jersey Servlet并指定自定义Application类,强制初始化顺序:

<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">
    <servlet>
        <servlet-name>CustomApplication</servlet-name>
        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
        <init-param>
            <param-name>jakarta.ws.rs.Application</param-name>
            <param-value>com.yourpackage.CustomApplication</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup> <!-- 强制启动时初始化,避免延迟加载导致的顺序问题 -->
    </servlet>
    <servlet-mapping>
        <servlet-name>CustomApplication</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

4. 排除WAR包中无需扫描的资源(可选)

如果需要保留部分自动扫描但排除特定类,可以使用@Exclude注解标注不需要自动注册的类,或设置ServerProperties.PROVIDER_EXCLUDE_CLASSES属性指定排除列表:

public class CustomApplication extends ResourceConfig {
    public CustomApplication() {
        // 指定排除的类
        property(ServerProperties.PROVIDER_EXCLUDE_CLASSES, 
                 Arrays.asList(UnwantedFilter.class, UnusedResource.class));
        // 手动注册需要的类
        register(AuthFilter.class);
        // ...
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 20:25:58