如何在Jersey(HK2)资源类中注入Session Scope作用域对象?
如何在Jersey资源类中注入Session Scope作用域的对象
当然可以实现!要让同一会话内的所有Jersey请求共用同一个对象,我们有两种主流方案,下面一步步给你拆解清楚:
方法1:结合CDI的@SessionScoped注解(推荐,更简洁)
如果你的项目已经集成了CDI(比如使用Jakarta EE、Spring Boot + Jersey),这是最省心的实现方式:
标记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方法 }在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(); } }确保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:
定义会话对象类
先创建你的会话数据类,不需要特殊注解:public class UserSessionData { private String username; // 省略getter、setter方法 }实现自定义注入逻辑
我们需要创建一个工厂类从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; } }在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; } }在资源类中使用自定义注解注入
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
相关产品推荐
相关产品推荐

