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

如何在Jersey(HK2)资源类中注入Session Scope作用域对象?

如何在Jersey资源类中注入Session Scope作用域的对象

当然可以实现!要让同一会话内的所有Jersey请求共用同一个对象,我们有两种主流方案,下面一步步给你拆解清楚:

方法1:结合CDI的@SessionScoped注解(推荐,更简洁)

如果你的项目已经集成了CDI(比如使用Jakarta EE、Spring Boot + Jersey),这是最省心的实现方式:

  1. 标记Session作用域的Bean
    给你要注入的类加上CDI的@SessionScoped注解,同时添加@Named让它成为可被注入的CDI Bean,注意必须实现Serializable接口(Servlet容器可能会序列化会话对象到磁盘):

    import jakarta.enterprise.context.SessionScoped;
    import jakarta.inject.Named;
    import java.io.Serializable;
    
    @Named
    @SessionScoped
    public class UserSessionData implements Serializable {
        private String username;
        
        // 省略getter、setter方法
    }
    
  2. 在Jersey资源类中直接注入
    用@Inject注解把这个Session作用域的对象注入到资源类里,同一个JSESSIONID对应的会话中,所有请求都会拿到同一个实例:

    import jakarta.inject.Inject;
    import jakarta.ws.rs.GET;
    import jakarta.ws.rs.Path;
    
    @Path("/user")
    public class UserResource {
        @Inject
        private UserSessionData userSessionData;
    
        @GET
        @Path("/username")
        public String getUsername() {
            return userSessionData.getUsername();
        }
    }
    
  3. 确保Jersey启用CDI支持
    纯Jersey项目需要在配置类中开启CDI扫描,比如:

    import jakarta.ws.rs.ApplicationPath;
    import jakarta.ws.rs.core.Application;
    
    @ApplicationPath("/api")
    public class JerseyConfig extends Application {
        // CDI会自动扫描并识别@SessionScoped的Bean
    }
    

方法2:自定义Jersey作用域绑定(不依赖CDI)

如果你的项目不想引入CDI依赖,可以通过自定义Jersey的注入提供者来实现Session Scope:

  1. 定义会话对象类
    先创建你的会话数据类,不需要特殊注解:

    public class UserSessionData {
        private String username;
        
        // 省略getter、setter方法
    }
    
  2. 实现自定义注入逻辑
    我们需要创建一个工厂类从HttpSession中获取/创建对象,再通过自定义注解标记注入点:

    import jakarta.servlet.http.HttpSession;
    import jakarta.ws.rs.core.Context;
    import jakarta.ws.rs.ext.Provider;
    import org.glassfish.hk2.api.Factory;
    import org.glassfish.hk2.api.InjectionResolver;
    import org.glassfish.hk2.api.TypeLiteral;
    
    // 自定义注解,标记需要Session注入的字段/参数
    @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
    @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.PARAMETER})
    public @interface SessionScopedInject {}
    
    // 工厂类:从HttpSession中获取或创建会话对象
    @Provider
    public class UserSessionDataFactory implements Factory<UserSessionData> {
        @Context
        private HttpSession session;
    
        @Override
        public UserSessionData provide() {
            UserSessionData data = (UserSessionData) session.getAttribute("UserSessionData");
            if (data == null) {
                data = new UserSessionData();
                session.setAttribute("UserSessionData", data);
            }
            return data;
        }
    
        @Override
        public void dispose(UserSessionData instance) {
            // 无需手动销毁,Session过期时容器会自动处理
        }
    }
    
    // 解析器:处理自定义的@SessionScopedInject注解
    public class SessionScopedInjectResolver implements InjectionResolver<SessionScopedInject> {
        private final UserSessionDataFactory factory;
    
        public SessionScopedInjectResolver(UserSessionDataFactory factory) {
            this.factory = factory;
        }
    
        @Override
        public Object resolve(org.glassfish.hk2.api.InjectionPoint injectionPoint, org.glassfish.hk2.api.ServiceHandle<?> root) {
            return factory.provide();
        }
    
        @Override
        public boolean isConstructorParameterIndicator() { return false; }
        @Override
        public boolean isMethodParameterIndicator() { return false; }
    }
    
  3. 在Jersey配置类中绑定提供者
    把自定义的工厂和解析器注册到Jersey的上下文里:

    import org.glassfish.hk2.utilities.binding.AbstractBinder;
    import jakarta.ws.rs.ApplicationPath;
    import jakarta.ws.rs.core.Application;
    import java.util.HashSet;
    import java.util.Set;
    
    @ApplicationPath("/api")
    public class JerseyConfig extends Application {
        @Override
        public Set<Class<?>> getClasses() {
            Set<Class<?>> classes = new HashSet<>();
            classes.add(UserResource.class);
            classes.add(UserSessionDataFactory.class);
            return classes;
        }
    
        @Override
        public Set<Object> getSingletons() {
            Set<Object> singletons = new HashSet<>();
            singletons.add(new AbstractBinder() {
                @Override
                protected void configure() {
                    bind(UserSessionDataFactory.class).to(UserSessionData.class);
                    bind(new SessionScopedInjectResolver(new UserSessionDataFactory()))
                            .to(new TypeLiteral<InjectionResolver<SessionScopedInject>>() {})
                            .in(org.glassfish.hk2.api.Singleton.class);
                }
            });
            return singletons;
        }
    }
    
  4. 在资源类中使用自定义注解注入

    import jakarta.ws.rs.GET;
    import jakarta.ws.rs.Path;
    
    @Path("/user")
    public class UserResource {
        @SessionScopedInject
        private UserSessionData userSessionData;
    
        @GET
        @Path("/username")
        public String getUsername() {
            return userSessionData.getUsername();
        }
    }
    

关键注意事项

  • 两种方案都依赖Servlet容器的会话管理,JSESSIONID Cookie的生成、传递完全由容器自动处理,不需要你手动干预。
  • Session作用域对象的生命周期和Servlet会话一致:会话创建时初始化,会话超时或用户注销时销毁。
  • 使用CDI方案时,确保项目依赖中包含CDI相关库(比如jakarta.enterprise.cdi-api)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 03:40:12