关于session.invalidate()、request.getSession(boolean)的一点实验

本文探讨了在JBOSS环境下出现“Session already invalidated”错误的原因,并提供了两种解决方案:一是通过异常处理来规避错误;二是深入解析了session的工作机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天发现jboss给我丢出了一个错误。
"Session already invlidated!"

于是开始寻找这个BUG的原因,后来发现时我代码里有一句session.invalidate();
由于session是有效期的,也就是说web容器会在有效期到了之后把到期的session彻底清除。
但是我写这段代码的时候显然并没有考虑到这个。所以当一个失效的session被我执行invalidate()操作时报错了这个错误。

解决办法。

思路1:
session.invalidate()执行前先判断这个session是否已经invalid。
google了一下,看了下API,session并没有提供相关的方法,只能间接通过HttpSessionListener监听session的销毁然后将其保存至某变量实现。显然,这不是我想要的。

思路2:
利用异常,既然你抛异常,那我就捕捉异常。
对这句话加一个try{}catch{},显然,catch后就知道这个session已经invalid.

问题解决了,顺便研究了一下session的机制。通过跟踪发现,session.invalidate()方法只是将session得isValid属性设置为了false,而此时的session并没有销毁,其他属性如id等依然存有数据。但是我们看一下API的描述

Java代码
invalidate()
Invalidates this session then unbinds any objects bound to it.
invalidate()
Invalidates this session then unbinds any objects bound to it.
这里不仅仅是把isValid设置为了false,还解除这个session和其他任何对象的关联,也就是说,这个session虽然是存在的,但是是游离的,是孤立的。

所以,通过request.getSession(false);你将得到的是null,虽然session存在,但已和request解除绑定了。

那么为什么会这样?

我没有去看源代码,但是可以推理,session是由web容器管理的,众所周知,大量的对象创建和销毁操作会消耗很多的资源,所以这个session通过一个maxInactiveInterval属性来缓存在web容器里,这样当你再有效期内重新请求web容器,则web容器只需要isValid属性恢复为true,然后这个session又可以分配给你了。

我做了个实验,代码如下

Java代码
HttpSession currentsession = request
.getSession();
request.getSession().invalidate();
request.getSession(false);
HttpSession currentsession = request
.getSession();
request.getSession().invalidate();
request.getSession(false);
我在1分钟内执行了上面代码两次,两次获得的session id是一致的。

再次声明小弟没有看源代码,这里只是根据实验推测而已,如有不对的地方,还望指正。

最后指出一点

Java代码
sessionDestroyed
public void sessionDestroyed(HttpSessionEvent se)
Notification that a session was invalidated.
Parameters:
se - the notification event
sessionDestroyed
public void sessionDestroyed(HttpSessionEvent se)
Notification that a session was invalidated.
Parameters:
se - the notification event


sessionDestroyed事件只要session invalidated就会触发,当然web容器彻底销毁session的时候也会触发。


---------------------------------------------------
request.getSession().invalidate();

解除session与对象间的关联,此时session处于游离状态
(一)项目构建 使用 Maven 构建项目,在pom.xml中引入 Spring MVC 相关依赖,如spring - webmvc,配置 Servlet、JSP 等相关依赖,确保项目具备 Spring MVC 运行环境 。示例关键依赖配置: <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring - webmvc</artifactId> <version>5.3.18</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet - api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>javax.servlet.jsp - api</artifactId> <version>2.3.3</version> <scope>provided</scope> </dependency> <!-- 其他如JSTL等依赖按需添加 --> </dependencies> (二)Spring MVC 配置 DispatcherServlet 配置:在 Web.xml(或 Servlet 3.0 + 环境下用 Java 配置)中配置 DispatcherServlet,指定 Spring MVC 配置文件位置,使其能拦截处理请求 。示例 Web.xml 配置: <servlet> <servlet - name>springmvc</servlet - name> <servlet - class>org.springframework.web.servlet.DispatcherServlet</servlet - class> <init - param> <param - name>contextConfigLocation</param - name> <param - value>classpath:springmvc - servlet.xml</param - value> </init - param> <load - on - startup>1</load - on - startup> </servlet> <servlet - mapping> <servlet - name>springmvc</servlet - name> <url - pattern>/</url - pattern> </servlet - mapping> Spring MVC 配置文件(springmvc - servlet.xml):开启组件扫描(扫描控制器等组件)、视图解析器(配置前缀后缀,解析 JSP 视图)、开启注解驱动等 。示例: <context:component - scan base - package="com.example.controller"/> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB - INF/views/"/> <property name="suffix" value=".jsp"/> </bean> <mvc:annotation - driven/> 三)功能模块实现 1. 用户登录验证 o 控制器(Controller):创建LoginController,编写方法处理/login请求(对应login.jsp表单提交),接收用户名、密码参数(可通过@RequestParam注解),进行简单验证(如硬编码用户名 “admin”、密码 “123” 模拟,实际可连数据库查询),验证成功用return "redirect:/main";重定向到主页控制器方法,失败则带错误信息(通过model.addAttribute)返回login视图(即login.jsp )。示例代码: package com.example.controller; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @Controller public class LoginController { @RequestMapping("/login") public String login(@RequestParam("username") String username, @RequestParam("password") String password, Model model) { if ("admin".equals(username) && "123".equals(password)) { return "redirect:/main"; } else { model.addAttribute("error", "用户名或密码错误"); return "login"; } } 2. 登录页面(login.jsp):使用 Spring 表单标签库(需引入相关标签库)或普通 HTML 表单,提交到/login,显示错误信息(通过 EL 表达式${error} )。示例关键代码: <%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %> <html> <body> <form:form action="/login" method="post"> 用户名:<form:input path="username"/><br> 密码:<form:password path="password"/><br> <input type="submit" value="登录"> </form:form> <c:if test="${not empty error}"> <p style="color:red">${error}</p> </c:if> </body> </html> 3. 访问控制(拦截器实现) o 创建拦截器类:实现HandlerInterceptor接口,在preHandle方法中检查会话是否有用户登录标识(如session.getAttribute("user") ,假设登录成功时在会话存user对象),无则redirect到/login并返回false拦截,有则返回true放行 。示例: package com.example.interceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.HandlerInterceptor; public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Object user = request.getSession().getAttribute("user"); if (user == null) { response.sendRedirect(request.getContextPath() + "/login"); return false; } return true; } } 4. 配置拦截器:在 Spring MVC 配置文件(springmvc - servlet.xml)中注册拦截器,指定拦截路径(如/main )。示例: <mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/main"/> <bean class="com.example.interceptor.LoginInterceptor"/> </mvc:interceptor> </mvc:interceptors> 5. 会话管理(登出功能) o 控制器方法:在MainController中编写处理登出的方法,调用session.invalidate()销毁会话,然后redirect到登录页 。示例: package com.example.controller; import javax.servlet.http.HttpSession; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class MainController { @RequestMapping("/main") public String main() { return "main"; } @RequestMapping("/logout") public String logout(HttpSession session) { session.invalidate(); return "redirect:/login"; } } 6. 主页(main.jsp):添加 “退出” 链接, href 指向/logout 。示例: <html> <body> <h1>欢迎访问主页</h1> <a href="${pageContext.request.contextPath}/logout">退出</a> </body> </html>
最新发布
06-05
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值