【小家Spring】Spring MVC之RequestContextHolder和LocaleContextHolder的使用详解以及使用误区

简介: 【小家Spring】Spring MVC之RequestContextHolder和LocaleContextHolder的使用详解以及使用误区

前言


在Java Web的开发中,我们大都执行着三层的开发模式(Controller、Service、Dao)。然后很少有人知道这三层的职责便捷在哪?

所以不乏经常遇到这样的问题:我这块逻辑该写在哪呢?我相信大多数初、中甚至高级程序员也分不太清楚,逻辑分层有点信手拈来,所以最终写成了后辈们眼中的“屎”,哈哈当然代码组织结构不是本文讨论的范畴~~~


在实际开发中:有不少小伙伴想在Service层或者某个工具类层里获取HttpServletRequest对象,甚至response的都有。

其中一种方式是,把request当作入参,一层一层的传递下去。不过这种有点费劲,且做起来很不优雅。这里介绍另外一种方案:RequestContextHolder,任意地方使用如下代码:

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


类似的,LocaleContextHolder是用来处理Local的上下文容器

RequestContextHolder使用以及源码分析


RequestContextHolder顾名思义,持有上下文的Request容器.使用是很简单的,它所有方法都是static的


该类主要维护了两个全局容器(基于ThreadLocal):

  // jsf是JSR-127标准的一种用户界面框架  过时的技术,所以此处不再做讨论
  private static final boolean jsfPresent =
      ClassUtils.isPresent("javax.faces.context.FacesContext", RequestContextHolder.class.getClassLoader());
  //现成和request绑定的容器
  private static final ThreadLocal<RequestAttributes> requestAttributesHolder =
      new NamedThreadLocal<>("Request attributes");
  // 和上面比较,它是被子线程继承的request   Inheritable:可继承的
  private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder =
      new NamedInheritableThreadLocal<>("Request context");


现在主要是她的一些get/set方法之类的:

  public static void resetRequestAttributes() {
    requestAttributesHolder.remove();
    inheritableRequestAttributesHolder.remove();
  }
  // 把传入的RequestAttributes和当前线程绑定。 注意这里传入false:表示不能被继承
  public static void setRequestAttributes(@Nullable RequestAttributes attributes) {
    setRequestAttributes(attributes, false);
  }
  //兼容继承和非继承  只要得到了就成
  public static RequestAttributes getRequestAttributes() {
    RequestAttributes attributes = requestAttributesHolder.get();
    if (attributes == null) {
      attributes = inheritableRequestAttributesHolder.get();
    }
    return attributes;
  }
  //在没有jsf的时候,效果完全同getRequestAttributes()  因为jsf几乎废弃了,所以效果可以说一致
  public static RequestAttributes currentRequestAttributes() throws IllegalStateException;

相关使用


DispatcherServlet在处理请求的时候,父类FrameworkServlet#processRequest就有向RequestContextHolder初始化绑定一些通用参数的操作,这样子使用者可以在任意地方,拿到这些公用参数了,可谓特别的方便。


在下面这篇博文讲解Spring MVC执行流程 源码分析中,就明确的讲述到了它的初始化过程~


小伙伴可以先自行先思考一个问题:request和response是怎么样设置进去的呢?(下文会分解)


另外监听器org.springframework.web.context.request.RequestContextListener(它是一个ServletRequestListener)里也有所体现,我们只需要配置上此监听器即可(因为DispatcherServlet里有处理,所以此监听器加不加,无所谓了~)


使用误区


场景描述一:在一个商品编辑页面,提交一个有附件的表单,这个时候通过RequestHolder.getRequest().getParameter()得不到参数值,这是为何?

其实使用过的我们发现,这么操作大部分情况下都是好使的,但是如果是文件上传,在DispatcherServlet里会把request包装成MultipartHttpServletRequest,同时content-type为multipart/form-data,因此这个时候getParameter()就失效了~


根本原因:checkMultipart()方法返回的是new出来的一个新的request,所以根本就不再是原来的引用了


场景描述二:在自己新启的线程里,是不能使用request对象的,当然也就不能使用RequestContextHolder去获取到请求域对象了,需要稍加注意


相关类:RequestAttributes


RequestAttributes该接口的定义了一些比如get/setAttribute()的便捷方法。它有很多子类,比如我们最常用的ServletRequestAttributes有较大的扩展,里面代理了request和response很多方法:

  public final HttpServletRequest getRequest() {
    return this.request;
  }
  @Nullable
  public final HttpServletResponse getResponse() {
    return this.response;
  }
  @Override
  public Object getAttribute / setAttribute(String name, int scope) { ... }
  getAttributeNames;

ServletWebRequestServletRequestAttributes的子类,还实现了接口NativeWebRequest(提供一些获取Native Request的方法,其实没太大作用):它代理得就更加的全一些,比如:


  @Nullable
  public HttpMethod getHttpMethod() {
    return HttpMethod.resolve(getRequest().getMethod());
  }
  @Override
  @Nullable
  public String getHeader(String headerName) {
    return getRequest().getHeader(headerName);
  }
  getHeaderValues/getParameter/... 等等一些列更多的代理方法


DispatcherServletWebRequest继续继承的子类,没什么好说的,唯一就是复写了此方法:

  @Override
  public Locale getLocale() {
    return RequestContextUtils.getLocale(getRequest());
  }

StandardServletAsyncWebRequest这个和异步拦截器相关,属于异步上下文范畴,此处不做讨论。


LocaleContextHolder使用以及源码分析


这个比上面就更简单些,是来做本地化、国际化的上下文容器。


  private static final ThreadLocal<LocaleContext> localeContextHolder =
      new NamedThreadLocal<>("LocaleContext");
  private static final ThreadLocal<LocaleContext> inheritableLocaleContextHolder =
      new NamedInheritableThreadLocal<>("LocaleContext");
  //没有手动调用setDefaultLocale,取值为  Locale#getDefault()
  private static Locale defaultLocale;
  //同上 默认取值为TimeZone.getDefault()
  private static TimeZone defaultTimeZone;


几乎源码过程同RequestContextHolder,只需要注意一个方法:


// 我们可以直接从请求域拿到Local上下文,但是也是可以自己传进来的。。。
public static Locale getLocale(@Nullable LocaleContext localeContext) { ... }

总结


其实这两个类也可以作为我们的工具来使用,我们集成的时候也可以使用Spring提供的两个类。

以小见大,优秀之所以优秀,是因为Spring确实做到了方便、快捷的编码环境,解放coder,它做了很多。当然人无完人,没有完美的东西,深入理解后我们也会发现,其实优秀如Spring,里面还是有些我们可以发挥,补充的地方


相关文章
|
6月前
|
前端开发 Java 测试技术
微服务——SpringBoot使用归纳——Spring Boot中的MVC支持——@RequestParam
本文介绍了 `@RequestParam` 注解的使用方法及其与 `@PathVariable` 的区别。`@RequestParam` 用于从请求中获取参数值(如 GET 请求的 URL 参数或 POST 请求的表单数据),而 `@PathVariable` 用于从 URL 模板中提取参数。文章通过示例代码详细说明了 `@RequestParam` 的常用属性,如 `required` 和 `defaultValue`,并展示了如何用实体类封装大量表单参数以简化处理流程。最后,结合 Postman 测试工具验证了接口的功能。
315 0
微服务——SpringBoot使用归纳——Spring Boot中的MVC支持——@RequestParam
|
6月前
|
JSON 前端开发 Java
微服务——SpringBoot使用归纳——Spring Boot中的MVC支持——@RequestBody
`@RequestBody` 是 Spring 框架中的注解,用于将 HTTP 请求体中的 JSON 数据自动映射为 Java 对象。例如,前端通过 POST 请求发送包含 `username` 和 `password` 的 JSON 数据,后端可通过带有 `@RequestBody` 注解的方法参数接收并处理。此注解适用于传递复杂对象的场景,简化了数据解析过程。与表单提交不同,它主要用于接收 JSON 格式的实体数据。
477 0
|
6月前
|
前端开发 Java 微服务
微服务——SpringBoot使用归纳——Spring Boot中的MVC支持——@PathVariable
`@PathVariable` 是 Spring Boot 中用于从 URL 中提取参数的注解,支持 RESTful 风格接口开发。例如,通过 `@GetMapping(&quot;/user/{id}&quot;)` 可以将 URL 中的 `{id}` 参数自动映射到方法参数中。若参数名不一致,可通过 `@PathVariable(&quot;自定义名&quot;)` 指定绑定关系。此外,还支持多参数占位符,如 `/user/{id}/{name}`,分别映射到方法中的多个参数。运行项目后,访问指定 URL 即可验证参数是否正确接收。
290 0
|
6月前
|
JSON 前端开发 Java
微服务——SpringBoot使用归纳——Spring Boot中的MVC支持——@RequestMapping
@RequestMapping 是 Spring MVC 中用于请求地址映射的注解,可作用于类或方法上。类级别定义控制器父路径,方法级别进一步指定处理逻辑。常用属性包括 value(请求地址)、method(请求类型,如 GET/POST 等,默认 GET)和 produces(返回内容类型)。例如:`@RequestMapping(value = &quot;/test&quot;, produces = &quot;application/json; charset=UTF-8&quot;)`。此外,针对不同请求方式还有简化注解,如 @GetMapping、@PostMapping 等。
257 0
|
6月前
|
JSON 前端开发 Java
微服务——SpringBoot使用归纳——Spring Boot中的MVC支持——@RestController
本文主要介绍 Spring Boot 中 MVC 开发常用的几个注解及其使用方式,包括 `@RestController`、`@RequestMapping`、`@PathVariable`、`@RequestParam` 和 `@RequestBody`。其中重点讲解了 `@RestController` 注解的构成与特点:它是 `@Controller` 和 `@ResponseBody` 的结合体,适用于返回 JSON 数据的场景。文章还指出,在需要模板渲染(如 Thymeleaf)而非前后端分离的情况下,应使用 `@Controller` 而非 `@RestController`
201 0
|
2月前
|
前端开发 Java API
Spring Cloud Gateway Server Web MVC报错“Unsupported transfer encoding: chunked”解决
本文解析了Spring Cloud Gateway中出现“Unsupported transfer encoding: chunked”错误的原因,指出该问题源于Feign依赖的HTTP客户端与服务端的`chunked`传输编码不兼容,并提供了具体的解决方案。通过规范Feign客户端接口的返回类型,可有效避免该异常,提升系统兼容性与稳定性。
177 0
|
2月前
|
SQL Java 数据库连接
Spring、SpringMVC 与 MyBatis 核心知识点解析
我梳理的这些内容,涵盖了 Spring、SpringMVC 和 MyBatis 的核心知识点。 在 Spring 中,我了解到 IOC 是控制反转,把对象控制权交容器;DI 是依赖注入,有三种实现方式。Bean 有五种作用域,单例 bean 的线程安全问题及自动装配方式也清晰了。事务基于数据库和 AOP,有失效场景和七种传播行为。AOP 是面向切面编程,动态代理有 JDK 和 CGLIB 两种。 SpringMVC 的 11 步执行流程我烂熟于心,还有那些常用注解的用法。 MyBatis 里,#{} 和 ${} 的区别很关键,获取主键、处理字段与属性名不匹配的方法也掌握了。多表查询、动态
110 0
|
2月前
|
JSON 前端开发 Java
第05课:Spring Boot中的MVC支持
第05课:Spring Boot中的MVC支持
148 0
|
8月前
|
SQL Java 数据库连接
对Spring、SpringMVC、MyBatis框架的介绍与解释
Spring 框架提供了全面的基础设施支持,Spring MVC 专注于 Web 层的开发,而 MyBatis 则是一个高效的持久层框架。这三个框架结合使用,可以显著提升 Java 企业级应用的开发效率和质量。通过理解它们的核心特性和使用方法,开发者可以更好地构建和维护复杂的应用程序。
359 29
|
开发框架 前端开发 .NET
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
371 0

热门文章

最新文章