Spring Boot中Filter类获取会话级UserContext对象遇阻求解决方案
解决Spring Boot Filter中无法获取Session Scope Bean的问题
针对你遇到的「Filter中获取@Scope("session")的UserContext时提示‘Scope 'session' is not active for the current thread’」的问题,以下是几个可行的解决思路:
1. 手动激活会话作用域再获取Bean
Spring Boot中会话作用域默认在DispatcherServlet处理请求时才会激活,Filter阶段直接调用会报错。可以手动触发会话作用域的激活:
public class YourFilter implements Filter { private WebApplicationContext applicationContext; @Override public void init(FilterConfig filterConfig) throws ServletException { applicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(filterConfig.getServletContext()); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpRequest = (HttpServletRequest) request; ServletRequestAttributes requestAttributes = new ServletRequestAttributes(httpRequest); RequestContextHolder.setRequestAttributes(requestAttributes); SessionScope sessionScope = applicationContext.getBean(SessionScope.class); try { // 激活会话作用域 sessionScope.startScope(); // 此时可以正常获取UserContext UserContext userContext = applicationContext.getBean(UserContext.class); // 执行你的业务逻辑 } finally { // 关闭作用域并清理上下文,避免线程污染 sessionScope.stopScope(); RequestContextHolder.resetRequestAttributes(); } chain.doFilter(request, response); } }
2. 调整Filter执行顺序,让它在Spring会话过滤器之后运行
Spring的SessionRepositoryFilter(默认或Redis缓存会话时都会用到)负责初始化会话上下文,你的Filter需要在它之后执行才能获取到激活的会话作用域。可以通过配置Filter的order实现:
方式一:配置类注册Filter时指定Order
@Configuration public class FilterRegistrationConfig { @Bean public FilterRegistrationBean<YourFilter> registerYourFilter() { FilterRegistrationBean<YourFilter> registrationBean = new FilterRegistrationBean<>(); registrationBean.setFilter(new YourFilter()); registrationBean.addUrlPatterns("/*"); // SessionRepositoryFilter的默认order是-2147483598,设为比它大的值即可保证在其后执行 registrationBean.setOrder(-2147483597); return registrationBean; } }
方式二:使用@Order注解(配合@WebFilter+@ServletComponentScan)
@WebFilter(urlPatterns = "/*") @Order(-2147483597) public class YourFilter implements Filter { // ... 实现你的业务逻辑 }
调整顺序后,你的Filter会在会话上下文初始化完成后执行,就能正常获取Session Scope的Bean了。
3. 绕过Spring作用域,直接操作HttpSession
如果不想依赖Spring的会话作用域管理,可以直接将UserContext存入HttpSession,在Filter中直接读取:
@Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpRequest = (HttpServletRequest) request; HttpSession session = httpRequest.getSession(); UserContext userContext = (UserContext) session.getAttribute("userContext"); if (userContext == null) { // 若不存在则初始化,可通过Spring容器获取实例 userContext = applicationContext.getBean(UserContext.class); session.setAttribute("userContext", userContext); } // 使用userContext处理业务 chain.doFilter(request, response); }
这种方式更直接,彻底避免了Spring作用域激活时机的问题,也能兼容不同Web容器。
另外需要说明:DelegatingFilterProxy只是让Filter被Spring容器管理,但不会改变会话作用域的激活时机;@EnableRedisHttpSession仅负责将会话数据持久化到Redis,同样不影响作用域的激活逻辑,所以这两个配置无法解决你的核心问题。
内容的提问来源于stack exchange,提问作者Rohit Grover
相关产品推荐
相关产品推荐

