SpringMVC中RequestContextHolder获取请求信息的方法

 更新时间:2020年04月03日 10:05:38   作者:昂蒂梵德  
这篇文章主要介绍了SpringMVC中RequestContextHolder获取请求信息的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

RequestContextHolder的作用是:

在Service层获取获取request和response信息

代码示例:

 ServletRequestAttributes attrs = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attrs.getRequest();

源码分析:

定义了两个ThreadLocal变量用来存储Request

 private static final ThreadLocal<RequestAttributes> requestAttributesHolder = new NamedThreadLocal("Request attributes");
  private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder = new NamedInheritableThreadLocal("Request context");

设置方法

  public static void setRequestAttributes(@Nullable RequestAttributes attributes) {
    setRequestAttributes(attributes, false);
  }

  public static void setRequestAttributes(@Nullable RequestAttributes attributes, boolean inheritable) {
    if (attributes == null) {
      resetRequestAttributes();
    } else if (inheritable) {
      inheritableRequestAttributesHolder.set(attributes);
      requestAttributesHolder.remove();
    } else {
      requestAttributesHolder.set(attributes);
      inheritableRequestAttributesHolder.remove();
    }

  }

是在SpringMVC处理Servlet的类FrameworkServlet的类中,doget/dopost方法,调用processRequest方法进行初始化上下文方法中initContextHolders设置进去的

 private void initContextHolders(HttpServletRequest request, @Nullable LocaleContext localeContext, @Nullable RequestAttributes requestAttributes) {
    if (localeContext != null) {
      LocaleContextHolder.setLocaleContext(localeContext, this.threadContextInheritable);
    }

    if (requestAttributes != null) {
      RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
    }

    if (this.logger.isTraceEnabled()) {
      this.logger.trace("Bound request context to thread: " + request);
    }

  }

再看一下请求信息怎么获取

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

    return attributes;
  }

解决疑问

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

首先分析RequestContextHolder这个类,里面有两个ThreadLocal保存当前线程下的request,关于ThreadLocal可以参考我的另一篇博文[Java学习记录--ThreadLocal使用案例]

//得到存储进去的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;
  }

2request和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`方法是多了很多.

到此这篇关于SpringMVC中RequestContextHolder获取请求信息的方法的文章就介绍到这了,更多相关SpringMVC RequestContextHolder请求信息内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 解决IDEA使用Spring Initializr创建项目时无法连接到https://start.spring.io的问题

    解决IDEA使用Spring Initializr创建项目时无法连接到https://start.spring.io的问

    这篇文章主要介绍了解决IDEA使用Spring Initializr创建项目时无法连接到https://start.spring.io的问题,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-04-04
  • Java IO之File 类详解

    Java IO之File 类详解

    这篇文章主要为大家介绍了vue组件通信的几种方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-01-01
  • 解决springboot文件配置端口不起作用(默认8080)

    解决springboot文件配置端口不起作用(默认8080)

    这篇文章主要介绍了解决springboot文件配置端口不起作用(默认8080),具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-08-08
  • Java+OpenCV实现人脸检测并自动拍照

    Java+OpenCV实现人脸检测并自动拍照

    这篇文章主要为大家详细介绍了Java+OpenCV实现人脸检测,并调用笔记本摄像头实时抓拍,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-07-07
  • SpringBoot Bean被加载时进行控制

    SpringBoot Bean被加载时进行控制

    很多时候我们需要根据不同的条件在容器中加载不同的Bean,或者根据不同的条件来选择是否在容器中加载某个Bean,这就是Bean的加载控制,一般我们可以通过编程式或注解式两种不同的方式来完成Bean的加载控制
    2023-02-02
  • 浅谈javaSE GUI (Action事件)

    浅谈javaSE GUI (Action事件)

    下面小编就为大家带来一篇浅谈javaSE GUI (Action事件)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-06-06
  • 深入浅出重构Mybatis与Spring集成的SqlSessionFactoryBean(上)

    深入浅出重构Mybatis与Spring集成的SqlSessionFactoryBean(上)

    通常来讲,重构是指不改变功能的情况下优化代码,但本文所说的重构也包括了添加功能。这篇文章主要介绍了重构Mybatis与Spring集成的SqlSessionFactoryBean(上)的相关资料,需要的朋友可以参考下
    2016-11-11
  • Java中的密码加密方式

    Java中的密码加密方式

    文章介绍了Java中使用MD5算法对密码进行加密的方法,以及如何通过加盐和多重加密来提高密码的安全性,MD5是一种不可逆的哈希算法,适合用于存储密码,因为其输出的摘要长度固定,且不容易发生碰撞,此外,通过加盐和多重加密,可以进一步增加密码的复杂性和安全性
    2025-01-01
  • Mybatis返回类型为Map时遇到的类型转化的异常问题

    Mybatis返回类型为Map时遇到的类型转化的异常问题

    这篇文章主要介绍了Mybatis返回类型为Map时遇到的类型转化的异常问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-12-12
  • java编程常用技术(推荐)

    java编程常用技术(推荐)

    下面小编就为大家带来一篇java编程常用技术(推荐)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-06-06

最新评论