5 动态修改Session实现
我们来实现下具体代码:
我们定义一个自定义session上下文MySessionContext
,里面定义HashMap属性来存储session信息,格式 sessionId :session对象;
package com.java1234.custom; import javax.servlet.http.HttpSession; import java.util.HashMap; /** * 自定义session上下文 * @author java1234_小锋 * @site www.java1234.com * @company 南通小锋网络科技有限公司 * @create 2021-08-05 10:39 */ public class MySessionContext { private static MySessionContext instance; // session map存储session 如果session较多,影响到系统性能的话,可以把用redis,key-value sessionId->session对象 session对象序列化 public static HashMap<String,HttpSession> sessionMap; private MySessionContext() { sessionMap = new HashMap<String,HttpSession>(); } /** * 单例 * @return */ public static MySessionContext getInstance() { if (instance == null) { instance = new MySessionContext(); } return instance; } /** * 添加session * @param session */ public synchronized void addSession(HttpSession session) { if (session != null) { System.out.println("session添加成功!"); sessionMap.put(session.getId(), session); } } /** * 删除session * @param session */ public synchronized void delSession(HttpSession session) { if (session != null) { System.out.println("session删除成功!"); sessionMap.remove(session.getId()); } } /** * 根据sessionId获取session * @param sessionID * @return */ public synchronized HttpSession getSession(String sessionID) { if (sessionID == null) { return null; } return sessionMap.get(sessionID); } }
新建session监听器SessionListener,监听session创建和销毁;
session创建的时候,把session信息存储到自定义session上下文;session销毁时,自定义session上下文中也删除掉该session;
注意,要加@WebListener注解
package com.java1234.listener; import com.java1234.custom.MySessionContext; import javax.servlet.annotation.WebListener; import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; /** * session监听器 * @author java1234_小锋 * @site www.java1234.com * @company 南通小锋网络科技有限公司 * @create 2021-08-05 10:43 */ @WebListener public class SessionListener implements HttpSessionListener { // 获取自定义session上下文实例 private MySessionContext msc = MySessionContext.getInstance(); /** * session创建事件 * @param se */ @Override public void sessionCreated(HttpSessionEvent se) { System.out.println("session创建"); HttpSession session = se.getSession(); msc.addSession(session); // 添加当前session到自定义session上下文 } /** * session销毁事件 * @param se */ @Override public void sessionDestroyed(HttpSessionEvent se) { System.out.println("session销毁"); HttpSession session = se.getSession(); //todo 要从数据库或者redis缓存把指定sessionId的用户session信息删除 msc.delSession(session); // 从自定义session上下文里删除当前session } }
在springboot项目中,要使得监听器有效,我们启动类要加@ServletComponentScan
注解
@ServletComponentScan,自动扫描带有(@WebServlet, @WebFilter, and @WebListener)注解的类,完成注册
到了这里,我们来测试下监听器是否有效;
启动项目,浏览器地址栏输入:http://localhost/user/login
说明监听器触发成功,session添加成功!
session的销毁方法触发,有以下三种方式;
1、超时(一般服务器设置超时时间为30分钟)服务器会销毁session;
2、点击控制台的红色按钮异常关闭服务器要销毁session
3、手动调用session的invalidate方法session.invalidate();
为了演示,我把session有效期设置成30秒;
server: port: 80 servlet: context-path: / session: timeout: 30s
我们执行模拟登录30秒后,只要不继续请求操作,30秒后,触发session销毁事件,session删除;
修改UserController,通过session获取servletContext上下文,存储用户session信息,格式 { userId : sessionId }
/** * 模拟用户登录 * @return */ @RequestMapping("/login") public String login(HttpSession session){ User uesr=new User(1,"java1234","123456"); session.setAttribute("currentUser",uesr); System.out.println(session.getId()); ServletContext servletContext = session.getServletContext(); // 模拟存储用户session信息到数据库 用application模拟 servletContext.setAttribute(String.valueOf(uesr.getId()),session.getId()); // key-value 用户id-sessionId return "success"; }
创建ManagerController测试:
package com.java1234.controller; import com.java1234.custom.MySessionContext; import com.java1234.entity.User; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.ServletContext; import javax.servlet.http.HttpSession; import java.util.HashMap; /** * 管理员Controller控制器 * @author java1234_小锋 * @site www.java1234.com * @company 南通小锋网络科技有限公司 * @create 2021-08-07 23:05 */ @RequestMapping("/manager") @RestController public class ManagerController { /** * 模拟用户登录 * @return */ @RequestMapping("/modifySession") public String modifySession(HttpSession session){ ServletContext servletContext = session.getServletContext(); String userId="1"; // 修改userId=1的用户 String sessionId = (String)servletContext.getAttribute(userId); // 从servletContext上下文 根据userId获取sessionId System.out.println("sessionId:"+sessionId); HashMap<String, HttpSession> sessionMap = MySessionContext.sessionMap; // 获取sessionMap HttpSession currentSession = sessionMap.get(sessionId); // 根据sessionId获取用户session User user = (User)currentSession.getAttribute("currentUser"); // 根据session得到用户信息 user.setLevel("vip"); // 修改内容 return "success"; } }
浏览器地址栏:http://localhost/user/login 模拟登录
浏览器地址栏:http://localhost/user/getUserInfo 获取用户信息
我们另外开一个浏览器地址栏:http://localhost/manager/modifySession 模拟管理员修改session
然后刷新getUserInfo;
我们发现session会话信息变了。测试成功!
6 高并发下的性能优化
在高并发下,同一时刻登录用户会很多,如果把session都放内存,会影响性能,甚至内存溢出;所以session可以存储到redis;
另外如下图的两个存储介质,也要存储到redis,以达到最佳性能;
7 完整源码+配套视频教程分享
源码和文档+视频我放在了Github和码云上面,大家有需要参考源码的直接pull下,IDEA工具;
码云地址:https://gitee.com/java_1234/modify-session
Github地址: https://github.com/java1234/modify-session
8 留了一点小作业
如果你是一个有理想的程序员,不妨可以升级下锋哥的实例;
第一:搭建一个高可用的redis集群环境;
第二:springboot+redis实现session存储到redis高速缓存;
第三:如上图的两个存储介质也存储到redis;
原文地址:老板发了我2千奖金,原因是我用Java实现管理员可以修改任意用户Session功能