SpringMVC--02--上下文工具类(RequestContextHolder)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • RequestContextHolder
    • 背景
    • 1.RequestContextHolder的使用
    • 2.request和response怎么和当前请求挂钩?
    • 3.request和response等是什么时候设置进去的?
  • 案例应用---用户信息文工具类
    • AuthSessionEntity
    • AuthSessionConstants
    • AuthContextHolder ---用户信息文工具类


RequestContextHolder

背景

  • 最近遇到的问题是在service获取request和response,正常来说在service层是没有request的,然而直接从controlller传过来的话解决方法太粗暴,后来发现了SpringMVC提供的RequestContextHolder遂去分析一番,并借此对SpringMVC的结构深入了解一下,后面会再发文章详细分析源码

1.RequestContextHolder的使用

  • RequestContextHolder顾名思义,持有上下文的Request容器.使用是很简单的,具体使用如下:

在这里插入图片描述

//两个方法在没有使用JSF的项目中是没有区别的
//RequestContextHolder.getRequestAttributes();
RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();//从session里面获取对应的值
String str = (String) requestAttributes.getAttribute("name",RequestAttributes.SCOPE_SESSION);HttpServletRequest request = ((ServletRequestAttributes)requestAttributes).getRequest();
HttpServletResponse response = ((ServletRequestAttributes)requestAttributes).getResponse();

2.request和response怎么和当前请求挂钩?

首先分析RequestContextHolder这个类,里面有两个ThreadLocal保存当前线程下的request,

//得到存储进去的request
private static final ThreadLocal<RequestAttributes> requestAttributesHolder =
new NamedThreadLocal<RequestAttributes>("Request attributes");//可被子线程继承的request
private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder =
new NamedInheritableThreadLocal<RequestAttributes>("Request context");

再看getRequestAttributes()方法,相当于直接获取ThreadLocal里面的值,这样就保证了每一次获取到的Request是该请求的request.

public static RequestAttributes getRequestAttributes() {RequestAttributes attributes = requestAttributesHolder.get();if (attributes == null) {attributes = inheritableRequestAttributesHolder.get();}return attributes;}

3.request和response等是什么时候设置进去的?

找这个的话需要对springMVC结构的DispatcherServlet的结构有一定了解才能准确的定位该去哪里找相关代码.

在IDEA中会显示如下的继承关系.

左边1这里是Servlet的接口和实现类.

右边2这里是使得SpringMVC具有Spring的一些环境变量和Spring容器.类似的XXXAware接口就是对该类提供Spring感知,简单来说就是如果想使用Spring的XXXX就要实现XXXAware,spring会把需要的东西传送过来.

那么剩下要分析的的就是三个类,简单看下源码

  1. HttpServletBean 进行初始化工作

  2. FrameworkServlet 初始化 WebApplicationContext,并提供service方法预处理请

  3. DispatcherServlet 具体分发处理.

那么就可以在FrameworkServlet查看到该类重写了service(),doGet(),doPost()…等方法,这些实现里面都有一个预处理方法processRequest(request, response);,所以定位到了我们要找的位置

查看processRequest(request, response);的实现,具体可以分为三步:

  1. 获取上一个请求的参数
  2. 重新建立新的参数
  3. 设置到XXContextHolder
  4. 父类的service()处理请求
  5. 恢复request
  6. 发布事
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
//获取上一个请求保存的LocaleContextLocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
//建立新的LocaleContextLocaleContext localeContext = buildLocaleContext(request);
//获取上一个请求保存的RequestAttributesRequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
//建立新的RequestAttributesServletRequestAttributes requestAttributes = buildRequestAttributes(request, 
response, previousAttributes);WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), 
new RequestBindingInterceptor());
//具体设置的方法initContextHolders(request, localeContext, requestAttributes);
try {doService(request, response);}
catch (ServletException ex) {
failureCause = ex;
throw ex;}
catch (IOException ex) {failureCause = ex;throw ex;}
catch (Throwable ex) {failureCause = ex;throw new NestedServletException("Request processing failed", ex);}
finally {
//恢复resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();}
if (logger.isDebugEnabled()) {
if (failureCause != null) {
this.logger.debug("Could not complete request", failureCause);}
else {
if (asyncManager.isConcurrentHandlingStarted()) {logger.debug("Leaving response open for concurrent processing");}
else {
this.logger.debug("Successfully completed request");}}}
//发布事件publishRequestHandledEvent(request, response, startTime, failureCause);}
}

再看initContextHolders(request, localeContext, requestAttributes)方法,把新的RequestAttributes设置进LocalThread,实际上保存的类型为ServletRequestAttributes,这也是为什么在使用的时候可以把RequestAttributes强转为ServletRequestAttributes.

private void initContextHolders(HttpServletRequest request, LocaleContext localeContext, RequestAttributes requestAttributes) {
if (localeContext != null) {LocaleContextHolder.setLocaleContext(localeContext, 
this.threadContextInheritable);}
if (requestAttributes != null) {RequestContextHolder.setRequestAttributes(requestAttributes, 
this.threadContextInheritable);}
if (logger.isTraceEnabled()) {logger.trace("Bound request context to thread: " + request);}
}

因此RequestContextHolder里面最终保存的为ServletRequestAttributes,这个类相比RequestAttributes方法是多了很多.

案例应用—用户信息文工具类

AuthSessionEntity

import lombok.Data;import java.util.List;
import java.util.Set;/****/
@Data
public class AuthSessionEntity {/*** 用户ID*/private Long userId;/*** 登录用户名*/private String userName;/*** 登录用户真实姓名*/private String realName;/*** 所属部门ID**/private Long organizeId;/*** 负责部门ID**/private Long chargedOrganizeId;/*** 部门名称**/private String organizeName;/*** 所属部门code*/private String organizeCode;/*** 负责部门code*/private String chargeOrganizeCode;/*** 用户角色集合**/private Set<Long> roles;/*** 角色名称**/private String roleName;/*** 是否首次登录**/private Integer accountStatus;/*** 是否为超管*/private boolean isSuperAdmin;/*** 用户权限列表**/private List<AuthPermissionEntity> menuVoList;private boolean firstLogin;private boolean headquartersTag;}

AuthSessionConstants

public class AuthSessionConstants {/*** 前端cookie中的key值**/public static final String AUTH_COOKIE_NAME = "SESSION";/*** 当前登录用户session信息*/public static final String CURRENT_USER = "CURRENT_USER";/*** 登录验证码*/public static final String LOGIN_CAPTCHA = "LOGIN_CAPTCHA";/*** 登录验证码有效期*/public static final long CAPTCHA_EXPIRED_TIME = 3L * 60L * 1000L;
}

AuthContextHolder —用户信息文工具类

  • RequestContextHolder [springMvc 的上下文工具类]
import com.alibaba.fastjson.JSON;
import com..framework.auth.entity.AuthSessionEntity;
import com..framework.exception.AppException;
import com..framework.exception.UnauthorizedException;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.Objects;/*** @description 认证配置上下文持有者,替代调原先的BaseController,作用参照springMvc 的上下文工具类* @see RequestContextHolder [springMvc 的上下文工具类]**/
public class AuthContextHolder {/*** 保存用户信息到当前会话*/public static void saveCurrentUserSession(AuthSessionEntity authSessionEntity) {if (authSessionEntity == null) {return;}HttpSession session = getCurrentSession();session.setAttribute(AuthSessionConstants.CURRENT_USER, JSON.toJSONString(authSessionEntity));}/*** 移除当前会话的用户信息*/public static void removeCurrentUserSession() {HttpSession session = getCurrentSession();session.removeAttribute(AuthSessionConstants.CURRENT_USER);session.invalidate();}/*** 获取当前用户登录信息*/public static AuthSessionEntity getCurrentUser() {HttpSession session = getCurrentSession();String currentUser = (String) session.getAttribute(AuthSessionConstants.CURRENT_USER);if (StringUtils.isBlank(currentUser)) {throw new UnauthorizedException(AuthError.USER_NOT_LOGIN);}return JSON.parseObject(currentUser, AuthSessionEntity.class);}/*** 当前用户是否登陆* @return*/public static boolean isLogin() {HttpSession session = getCurrentSession();String currentUser = (String) session.getAttribute(AuthSessionConstants.CURRENT_USER);return !StringUtils.isBlank(currentUser);}/*** 获取当前登录用户会话*/public static HttpSession getCurrentSession() {return getRequest().getSession();}public static Cookie getAuthCookie() {Cookie[] cookies = getRequest().getCookies();Cookie authCookie = null;for (Cookie cookie : cookies) {if (AuthSessionConstants.AUTH_COOKIE_NAME.equals(cookie.getName()))authCookie = cookie;}if (authCookie == null)throw new AuthException(AuthError.USER_NOT_LOGIN);return authCookie;}public static String getAuthSource() {return getRequest().getHeader(AuthFilter.X_SOURCE);}private static HttpServletRequest getRequest() {return ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/812395.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

最新常见的图数据库对比,选型,架构,性能对比

图数据库排名 地址&#xff1a;https://db-engines.com/en/ranking/graphdbms 知识图谱查询语言 SPARQL、Cypher、Gremlin、PGQL 和 G-CORE 语法 / 语义 / 特性SPARQLCypherGremlinPGQLG-CORE图模式匹配查询语法CGPCGPCGP(无可选)1CGPCGP语义子图同态、包 2无重复边、包 2子…

C语言之九九乘法表||素数||最小公倍数

一、九九乘法表 &#xff08;1&#xff09;思路 1、九九乘法表中存在三个变量&#xff0c;以 x1 ; x2 ; y 为例&#xff08;这里也可以使用两个变量&#xff0c;用x1和x2来表示y&#xff0c;方法一样&#xff09; 2、想好了变量之后&#xff0c;我们要想怎样将他实现呢&#x…

智能售货机:引领便捷生活

智能售货机&#xff1a;引领便捷生活 在这个科技迅速进步的时代&#xff0c;便捷已成为生活的必需。智能售货机作为技术与便利完美结合的产物&#xff0c;正逐渐改变我们的购物方式&#xff0c;为都市生活增添新的活力。 智能售货机的主要优势是它的极致便利性。不论是在地铁…

变量---

一、变量概述 1、什么是变量 变量是用于存放数据的容器。通过变量名 获取数据&#xff0c;甚至数据可以修改。 变量的本质&#xff1a;变量是程序在内存中申请的一块用来存放数据的空间。 二、变量的使用 变量在使用时分两步&#xff1a;1、声明变量 2、赋值 三、变量语法…

【c 语言】结构体的定义格式及变量初始化

&#x1f388;个人主页&#xff1a;豌豆射手^ &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏 &#x1f917;收录专栏&#xff1a;C语言 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共同学习、交流进步&…

Vue2 —— 学习(三)

目录 一、绑定 class 样式 &#xff08;一&#xff09;字符串写法 1.流程介绍 2.代码实现 &#xff08;二&#xff09;数组写法 1.流程介绍 2.代码实现 &#xff08;三&#xff09;对象写法 1.流程介绍 2.代码实现 二、绑定 style 样式&#xff08;了解&#xff…

备忘录模式:恢复对象状态的智能方式

在软件开发中&#xff0c;备忘录模式是一种行为型设计模式&#xff0c;它允许捕获并外部化对象的内部状态&#xff0c;以便在未来某个时刻可以将对象恢复到此状态。这种模式是撤销操作或者回滚操作的关键实现机制。本文将详细介绍备忘录模式的定义、实现、应用场景以及优缺点。…

【linux深入剖析】深入理解基础外设--磁盘以及理解文件系统

&#x1f341;你好&#xff0c;我是 RO-BERRY &#x1f4d7; 致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 &#x1f384;感谢你的陪伴与支持 &#xff0c;故事既有了开头&#xff0c;就要画上一个完美的句号&#xff0c;让我们一起加油 目录 前言1.磁盘物理结构2.磁盘…

SpringAI初体验之HelloWorld

目录 前言1.准备工作2.初始化项目3.解决问题3.1 Connection Time out 连接超时问题3.2 You exceeded your current quota 额度超限问题 4.访问调用5.总结 前言 在逛SpringBoot页面时突然看到页面上新增了一个SpringAI项目,于是试了一下&#xff0c;感觉还行。其实就是封装了各家…

Harmony鸿蒙南向外设驱动开发-Audio

Audio驱动概述 多媒体系统是物联网设备开发中不可缺少的一部分&#xff0c;Audio作为其中重要的一个模块&#xff0c;Audio驱动模型的构建显得尤为重要。 本文主要介绍基于HDF&#xff08;Hardware Driver Foundation&#xff09;驱动框架开发的Audio驱动&#xff0c;包括Aud…

是时候开启Copilot下一篇章:Microsoft AI

微软总裁兼首席执行官萨提亚纳德拉欢迎 Mustafa Suleyman 和 Karn Simonyan 加入微软公司&#xff0c;领导一个新成立的部门 —— Microsoft AI&#xff0c;旨在开发 Copilot 和其他的面向消费者的 AI 产品和研究。 Mustafa Suleyman 将担任 Microsoft AI 执行副总裁&#xf…

基于单片机的智能居家火灾报警系统

摘要:采用STC15L2K32单片机设计了一种智能火灾报警系统,它是控制中心与多个不同功能的探测模块构成,实现了一个中心、多点辐射的火灾检测和报警功能。 关键词:智能居家,火灾报警系统,单片机,模块化设计。 0 引言 近些年电子技术、计算机技术为火灾报警系统和灭火系统在…

idea keymap用eclipse的相关快捷键

idea快捷键用eclipse的方式 CtrlShiftR 搜索文件 shiftshift 全部文件/类搜索 CtrlH 全局搜索 CtrlO 快速打开Outline大纲视图 ctrle 查看recent窗口文件 ctrlt 快速进入接口的实现类 ctrlshiftf 格式化代码 altshiftr 变量或函数的重命名 ctrlshifto 移除无用的头文…

YOLOv8绝缘子边缘破损检测系统(可以从图片、视频和摄像头三种方式检测)

可检测图片和视频当中出现的绝缘子和绝缘子边缘是否出现破损&#xff0c;以及自动开启摄像头&#xff0c;进行绝缘子检测。基于最新的YOLO-v8训练的绝缘子检测模型和完整的python代码以及绝缘子的训练数据&#xff0c;下载后即可运行。&#xff08;效果视频&#xff1a;YOLOv8绝…

C# WinForm —— 项目目录结构

1. WinForm 应用程序项目 Properties&#xff1a;属性文件夹存放了一个自动生成的类文件AssemblyInfo.cs&#xff0c;保存了一些应用程序集的一些信息引用存放了一些为应用程序提供所需的&#xff0c;某些功能的一些程序集&#xff08;dll文件&#xff09;等添加引用&#xff…

MindOpt APL向量化建模语法的介绍与应用(2)

前言 在数据科学、工程优化和其他科学计算领域中&#xff0c;向量和矩阵的运算是核心组成部分。MAPL作为一种数学规划语言&#xff0c;为这些领域的专业人员提供了强大的工具&#xff0c;通过向量式和矩阵式变量声明以及丰富的内置数学运算支持&#xff0c;大大简化了数学建模…

Centos7 搭建Mongodb 分片集群4.0/ PSA(三成员副本集)

MongoDB 简介:1、优点和缺点:2、MongoDB适用的业务场景:Centos7 搭建Mongodb 分片集群一、安装MongoDB社区版4.01、配置程序包管理系统(`yum`)2、安装对应版本的MongoDB软件包。3、创建运行mongodb的目录并禁用SELinux4、修改文件打开数5、初始化系统5.1、创建config配置…

Docker 学习笔记(七):介绍 Dockerfile 相关知识,使用 Dockerfile 构建自己的 centos 镜像

一、前言 记录时间 [2024-4-12] 系列文章简摘&#xff1a; Docker学习笔记&#xff08;二&#xff09;&#xff1a;在Linux中部署Docker&#xff08;Centos7下安装docker、环境配置&#xff0c;以及镜像简单使用&#xff09; Docker 学习笔记&#xff08;三&#xff09;&#x…

【数据结构(五)】栈

❣博主主页: 33的博客❣ ▶️文章专栏分类:数据结构◀️ &#x1f69a;我的代码仓库: 33的代码仓库&#x1f69a; &#x1faf5;&#x1faf5;&#x1faf5;关注我带你学更多数据结构知识 目录 1.前言2.概念3.栈的使用4.栈的应用场景4.1有效的括号4.2逆波兰表达式4.3栈的压入弹…

VUE_H5页面跳转第三方地图导航,兼容微信浏览器

当前项目是uniapp项目&#xff0c;若不是需要替换uni.showActionSheet选择api onMap(address , organName , longitude 0, latitude 0){var ua navigator.userAgent.toLowerCase();var isWeixin ua.indexOf(micromessenger) ! -1;if(isWeixin) {const mapUrl_tx "…