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;
//获取上一个请求保存的LocaleContext
    LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
//建立新的LocaleContext
    LocaleContext localeContext = buildLocaleContext(request);
//获取上一个请求保存的RequestAttributes
    RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
//建立新的RequestAttributes
    ServletRequestAttributes 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();
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

softmax回归:多分类问题的解码器

随着人工智能技术的不断发展&#xff0c;分类问题在机器学习领域中的地位日益凸显。在众多分类算法中&#xff0c;softmax回归以其独特的优势和广泛的应用场景&#xff0c;成为了处理多分类问题的有力工具。本文将深入探讨softmax回归的原理、应用及其优缺点&#xff0c;以期为…

【好用】推荐10套后端管理系统前端模板

后台管理系统前端模板是开发者在构建后台管理系统时使用的一种工具&#xff0c;它提供了预先设计好的界面和组件&#xff0c;以帮助开发者快速搭建出功能完善、用户体验良好的管理系统。以下是V哥整理的10款流行的后台管理系统前端模板&#xff0c;它们基于不同的技术栈和设计理…

找出mongodb的jumbo块并进行分裂

https://www.cnblogs.com/abclife/p/15968628.html 根据这篇文档中的脚本&#xff0c;在我们自己的环境中跑了下&#xff0c;第一次跑的结果如下&#xff1a; 运行完上面跑出的split脚本后&#xff0c;还是存在jumbo块&#xff0c;第二次跑出的结果&#xff1a; 从上面结果可以…

【Vue3进阶】- 第2学堂小商城项目后端准备和接口文档

简介 在大多数前端项目开发中&#xff0c;都需要与后端进行接口交互&#xff0c;后端通常会以文档的形式提供接口信息&#xff0c;前端开发者通过阅读这些文档&#xff0c;了解后端接口的功能和使用方法&#xff0c;从而实现数据的获取和提交等功能。 第二学堂小商城教程后端…

古月·ROS2入门21讲——学习笔记

第一讲&#xff1a;ROS/ROS2是什么 1. ROS的诞生 对于越来越复杂的智能机器人系统&#xff0c;已经不是一个人或者一个团队可以独立完成的&#xff0c;如何高效开发机器人&#xff0c;是技术层面上非常重要的一个问题&#xff0c;针对这个问题&#xff0c;一群斯坦福大学的有…

CentOS7 boa服务器的搭建和配置

环境是CentOS7&#xff0c;但方法不局限于此版系统&#xff0c;应该是通用的。 具体步骤如下&#xff1a; 1. 下载boa源码 下载地址: Boa Webserver 下载后&#xff0c;进入压缩包所在目录&#xff0c;进行解压&#xff1a; tar xzf boa-0.94.13.tar.gz 2. 安装需要的工具b…

要申请开通融资融券账户,有那些条件?

1、什么是融资融券交易? 融资融券交易&#xff0c;又称信用交易&#xff0c;是指投资者向具有融资融券业务资格的证券公司提供担 保物&#xff0c;借入资金买入交易所上市证券&#xff08;融资交易&#xff09;或借入交易所上市证券并卖出&#xff08;融券交易&#xff09; 的…

如何在ADS中实现数据的导入和导出

1 MDIF接口 ADS提供了一种通用的MDIF格式文件&#xff0c;允许用户使用一个通用的数据接口实现导入和导出的功能&#xff0c;其Help文件中的简介如下&#xff1a; 2 数据的导入 实现数据导入功能之前&#xff0c;数据必须遵从一定的标准格式&#xff0c;如下图所示&#xff0c;…

联储降息预期落空打了谁的脸

美国 3 月消费者价格指数&#xff08;CPI&#xff09;于本周发布&#xff0c;最新数据全线高于预期。具体而言&#xff0c;美国劳工部周三公布的数据显示&#xff0c;美国 3 月消费者物价指数&#xff08;CPI&#xff09;同比上涨 3.5%&#xff0c;为 2023 年 9 月以来最高水平…

汇编基础-----通过x64dbg了解什么是堆栈

汇编基础-----通过x64dbg了解什么是堆栈 什么是堆栈 在汇编语言中&#xff0c;堆栈&#xff08;stack&#xff09;是一种用于存储临时数据和执行函数调用的内存结构。堆栈是一种后进先出&#xff08;Last-In-First-Out, LIFO&#xff09;的数据结构&#xff0c;通常用于保存函…

算法修炼之路之双指针含多道leetcode 经典题目

目录 前言 一&#xff1a;普通双指针 1.经典题目一 283移动0问题 分析 代码实现 2.经典题目二 1089复写0 分析 代码实现 二&#xff1a;解决成环类问题-快慢指针 经典例题一 202快乐数 分析 代码实现 三&#xff1a;左右相遇指针 经典例题一 11 盛最多水的容…

阿里云9元服务器租用收费价格表_免费云服务器领取

2024年最新阿里云服务器租用费用优惠价格表&#xff0c;轻量2核2G3M带宽轻量服务器一年61元&#xff0c;折合5元1个月&#xff0c;新老用户同享99元一年服务器&#xff0c;2核4G5M服务器ECS优惠价199元一年&#xff0c;2核4G4M轻量服务器165元一年&#xff0c;2核4G服务器30元3…

「代码之舞:选择成为程序员的兴趣与职业发展」

文章目录 每日一句正能量前言当初为什么会选择成为一名程序员&#xff1f;你觉得程序员是一个怎样的职业&#xff1f;你会如何看待「35 岁危机」这个话题&#xff1f;从事这个职业以来&#xff0c;分享一下你印象最深的一件事&#xff1f;对于即将入行的职场后辈们&#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配置…

CentOS 8服务器搭建L2TP服务器(over IPsec)操作指南

正文共&#xff1a;1234 字 14 图&#xff0c;预估阅读时间&#xff1a;2 分钟 之前发过把我自己的服务器搬上公网的文章&#xff08;我用100块钱把物理服务器放到了公网&#xff0c;省了几万块&#xff01;&#xff09;&#xff0c;当时L2TP拨号用的是网络上的解决方案&#x…

Java 集合Collection

集合的体系 Collection的结构体系 List系列集合&#xff1a;添加的元素是有序的、可重复、有索引。Set系列集合&#xff1a;无序、不重复、无索引 HashSet&#xff1a;无序、不重复、无索引LinkedHashSet:有序、不重复、无索引TreeSet&#xff1a;按照大小默认升序排序、不重复…

最新PDD批发Anti-Content参数逆向分析与算法还原

文章目录 1. 写在前面2. 接口分析3. 分析与扣代码 【&#x1f3e0;作者主页】&#xff1a;吴秋霖 【&#x1f4bc;作者介绍】&#xff1a;擅长爬虫与JS加密逆向分析&#xff01;Python领域优质创作者、CSDN博客专家、阿里云博客专家、华为云享专家。一路走来长期坚守并致力于Py…

Day:007(1) | Python爬虫:高效数据抓取的编程技术(scrapy框架使用)

Scrapy的介绍 Scrapy 是一个用于抓取网站和提取结构化数据的应用程序框架&#xff0c;可用于各种有用的应用程序&#xff0c;如数据挖掘、信息处理或历史存档。 尽管 Scrapy 最初是为网络抓取而设计的&#xff0c;但它也可用于使用API提取数据或用作通用网络爬虫。 Scrapy的优势…

海外媒体发稿:4种旅游业媒体套餐助你宣发推广-华媒舍

在现代社会中&#xff0c;旅游业发展迅速&#xff0c;竞争也变得日益激烈。为了让自己的旅游产品或服务脱颖而出&#xff0c;宣传和推广变得至关重要。有着强大传播力的媒体平台成为了旅游行业的一项重要资源。为了更好地推广旅游业&#xff0c;提高其影响力&#xff0c;有许多…

ABAP-CPI-Odata POST-create_deep_entity 多层嵌套的处理及CPI端的调用

该文章演示怎么在OData里,创建一个多套多的请求结构,传入数据处理后,返回多层级的处理结果;以及如何在CPI里写groovy脚本,去解析它;最后如何用postman模拟外围系统,调用CPI这个接口,从而去调用Odata接口 假如想用SAP Odata去实现传入多层级的数据,进行创建或者根据传入…