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请求信息内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

时间: 2020-04-03

利用SpringMVC过滤器解决vue跨域请求的问题

之前写过通过注释的方法解决跨域请求的方法,需要每次都在controll类使用注解,这次通过springmvc的拦截器解决: 继承SpringMVC的类HandlerInterceptor重写preHandle方法,这个方法会在到达 controll之前调用,如下 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

springmvc处理异步请求的示例

springmvc 3.2开始就支持servlet3.0的异步请求.平常我们请求一个controller一般都是同步的,如果在代码执行中,遇到耗时的业务操作,那servlet容器线程就会被锁死,当有其他请求进来的时候就会受堵了. springmvc3.2之后支持异步请求,能够在controller中返回一个Callable或者DeferredResult.当返回Callable的时候,大概的执行过程如下: 当controller返回值是Callable的时候,springmvc就会启动一个线程将

axios发送post请求springMVC接收不到参数的解决方法

axios发送post请求时,出现了参数后台接收不到的情况,分析了下请求,发现是请求头content-type不对,是application/json,正常应该是application/x-www-form-urlencoded. 解决方法有以下三种: 1.设置axios的默认请求头 //设置全局的 axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'; var instance = a

springMvc请求的跳转和传值的方法

forword跳转页面的三种方式: 1.使用serlvet /** * 使用forward跳转,传递基本类型参数到页面 * 注意: * 1.使用servlet原生API Request作用域 * */ @RequestMapping("/test") public String test(HttpServletRequest request,HttpServletResponse response){ String name = "张小三"; request.set

SpringMVC解析JSON请求数据问题解析

这几年都在搞前后端分离.RESTful风格,我们项目中也在这样用.前几天有人遇到了解析JSON格式的请求数据的问题,然后说了一下解析的方式,今天就写篇文章简单的分析一下后台对于JSON格式请求数据是怎么解析的. 先把例子的代码贴出来: 前端 <input type="button" value="测试JSON数据" onclick="testJSON()" /> <script type="text/javascrip

SpringMVC post请求中文乱码问题解决

这篇文章主要介绍了SpringMVC post请求中文乱码问题解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 我们在页面难免提交一些中文数据给后台处理,但是发现后台拿到的数据乱码,可以在每一个controller中都设置编码,但是太过于麻烦, 正确的解决办法应该是在web.xml中配置解决中文乱码的过滤器: 问题现象:控制台打印中文乱码如下: 解决办法如下: (web.xml中配置解决中文乱码的顾虑器CharacterEncodingFil

SpringMVC的REST风格的四种请求方式总结

一. 在HTTP 协议里面,四个表示操作方式的动词:GET.POST.PUT.DELETE. 它们分别对应四种基本操作: 1.GET ====== 获 取资源 2.POST ======新建资源 3.PUT======= 更新资源 4.DELETE==== 删除资源 二.REST:即 Representational State Transfer.(资源)表现层状态转化.是目前最流行的一种互联网软件架构.它结构清晰.符合标准.易于理解.扩展方便, 所以正得到越来越多网站的采用. 我们可以通过re

SpringMVC环境下实现的Ajax异步请求JSON格式数据

一 环境搭建 首先是常规的spring mvc环境搭建,不用多说,需要注意的是,这里需要引入jackson相关jar包,然后在spring配置文件"springmvc-servlet.xml"中添加json解析相关配置,我这里的完整代码如下: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schem

详解SpringMVC——接收请求参数和页面传参

spring接收请求参数: 1,使用HttpServletRequest获取 @RequestMapping("/login.do") public String login(HttpServletRequest request){ String name = request.getParameter("name") String pass = request.getParameter("pass") } 2,Spring会自动将表单参数注入到方

详解React-Router中Url参数改变页面不刷新的解决办法

问题 今天在写页面的时候发现一个问题,就是在React Router中使用了Url传参的功能,像这样: export class MainRouter extends React.Component { render() { return ( <BrowserRouter> <Switch> ... <Route exact path={'/channel/:channelId'} component={ChannelPerPage}/> ... </Switch

[Spring MVC]-详解SpringMVC的各种参数绑定方式

SpringMVC的各种参数绑定方式 1. 基本数据类型(以int为例,其他类似): Controller代码: @RequestMapping("saysth.do") public void test(int count) { } 表单代码: <form action="saysth.do" method="post"> <input name="count" value="10" t

详解springmvc 接收json对象的两种方式

最近学习了springmvc 接收json对象的两种方式,现在整理出来,具体如下: 1.以实体类方式接收 前端 ajax 提交数据: function fAddObj() { var obj = {}; obj['objname'] = "obj"; obj['pid'] = 1 ; $.ajax({ url: 'admin/Obj/addObj.do', method: 'post', contentType: 'application/json', // 这句不加出现415错误:U

详解Vue中一种简易路由传参办法

情景模拟: A页面中,有一些div是根据A中的book数据通过v-for生成的,比如item. 并且点击会根据路由跳转到B页面. 而跳转到B页面后,我需要A中的item. <div v-for="(item,index) in book" :class='{on:$route.path === `/${item.to}/`}' @click='toOther(item.to)'> </div> toOther(to,run) { if(this.$route.p

快速解决SpringMVC @RequestBody 用map接收请求参数的问题

一:遇到个跨域调用,因为传个我的参数不定,所以需要通过map来接收参数并进行签名验证等操作 理所当然的写出了下面的代码,但是发现map里并没有获取到传来的key-value值 @RequestMapping(value = "/callback", produces = "text/html;charset=UTF-8") @ResponseBody public String callback(@RequestBody Map<String, String&

Django模板标签中url使用详解(url跳转到指定页面)

django {% url %} 模板标签使用 inclusions/_archives.html ... {% for date in date_list %} <li> <a href="{% url 'blog:archive' date.year date.month %}" rel="external nofollow" > {{ date.year }} 年 {{ date.month }} 月 </a> </l

详解SpringMVC的url-pattern配置及原理剖析

xml里面配置标签: <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <display-name>Archetype Created Web Application</display-name> &

详解angularjs跨页面传参遇到的一些问题

上周写课程选择时间功能时需要将课程ID,星期,节次等参数传递给下一个页面,就查了查angularjs的ui-router跨页面传参,一开始是这样写的: 在app.js下添加 params:{'args':{}} 然后在起始页面的控制器中使用transtionTo或者go方法传递参数 最后在目标页面的控制器使用$stateParams接收参数,如下图,可知我需要传输的参数都传了过来 这样传参的好处就是方便灵活,但有一个不好的地方就是每次刷新完以后传递过来的参数都会丢失,是所以我最后放弃了这种写法,

详解SpringMVC从基础到源码

认识SpringMVC SpringMVC 框架是以请求为驱动,围绕 Servlet 设计,将请求发给控制器,然后通过模型对象,分派器来展示请求结果视图.其中核心类是 DispatcherServlet,它是一个 Servlet,顶层是实现的Servlet接口. SpringMVC 处理请求过程 客户端发起请求,会首先经过前端控制器 DispatcherServlet 进行转发,转发到 Handler Mapping DispatcherServlet 从 Handler Mapping 查找处