共享对象

  • 如果在每个线程中ThreadLocal.set()进去的东西本来就是多线程共享的同一个对象,比如static对象,那么多个线程的ThreadLocal.get()取得的还是这个共享对象本身,还是有并发访问问题

如果不使用ThreadLocal就可以解决问题,那么就不要强行使用

  • 例如在任务很少的时候,在局部变量中可以新建对象就可以解决问题,那么就不需要使用到ThreadLocal

优先使用框架的支持,而不是自己创造

  • 例如在Spring中,如果可以使用RequestContextHolder,那么就不需要自己维护ThreadLocal,因为自己可能会忘记调用remove()方法,造成内存泄漏

ThreadLocal在Spring中的实例分析

  • RequestContextHolder和DateTimeContextHolder中,看到里面使用了ThreadLocal
  • 每次HTTP请求都对应一个线程,线程之间相互隔离,这就是ThreadLocal的典型应用场景

在Web开发中,service层或者某个工具类中需要获取到HttpServletRequest对象还是比较常见的。一种方式是将HttpServletRequest作为方法的参数从controller层一直放下传递,不过这种有点费劲,且做起来不是优雅;还有另一种则是RequestContextHolder,直接在需要用的地方使用如下方式取HttpServletRequest即可,使用代码如下:

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

要理解上面的为何可以这么使用,需要理解两个问题:

  1. RequestContextHolder为什么能获取到当前的HttpServletRequest
  2. HttpServletRequest是在什么时候设置到RequestContextHolder

对于第1个问题,熟悉ThreadLocal的人应该很容易看出来这个是ThreadLocal的应用,这个类的原理在(ThreadLocal原理)有讲到,其实很类似上篇博文文末提到的UserContextHolder。

第2个问题应该属于spring-mvc的问题,这个是在spring-mvc执行时设置进去的

源码分析

首先我们先来看下RequestContextHolder的源码,源码如下:

public abstract class RequestContextHolder  {private static final ThreadLocal<RequestAttributes> requestAttributesHolder =new NamedThreadLocal<RequestAttributes>("Request attributes");private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder =new NamedInheritableThreadLocal<RequestAttributes>("Request context");public static void resetRequestAttributes() {requestAttributesHolder.remove();inheritableRequestAttributesHolder.remove();}public static void setRequestAttributes(RequestAttributes attributes) {setRequestAttributes(attributes, false);}//将RequestAttributes对象放入到ThreadLocal中,而HttpServletRequest和HttpServletResponse等则封装在RequestAttributes对象中,在此处就不对RequestAttributes这个类展开。反正我们需要知道的就是要获取RequestAttributes对象,然后再从RequestAttributes对象中获取到我们所需要的HttpServletRequest即可public static void setRequestAttributes(RequestAttributes attributes, boolean inheritable) {if (attributes == null) {resetRequestAttributes();}else {if (inheritable) {inheritableRequestAttributesHolder.set(attributes);requestAttributesHolder.remove();}else {requestAttributesHolder.set(attributes);inheritableRequestAttributesHolder.remove();}}}public static RequestAttributes getRequestAttributes() {RequestAttributes attributes = requestAttributesHolder.get();if (attributes == null) {attributes = inheritableRequestAttributesHolder.get();}return attributes;}}

那么在spring-mvc中是怎么实现的呢,我们来简单分析的,想了解具体机制的可以去看看spring-mvc的源码。

我们看下FrameworkServlet这个类,也就是DispatcherServlet的父类,里面有个processRequest方法,根据方法名称我们也可以大概了解到这个是方法用于处理请求的。

protected final void processRequest(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {long startTime = System.currentTimeMillis();Throwable failureCause = null;LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();LocaleContext localeContext = buildLocaleContext(request);RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());//将RequestAttributes设置到RequestContextHolderinitContextHolders(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 {//重置RequestContextHolder之前设置RequestAttributesresetContextHolders(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);}}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);}}

简单看下源码,我们可以知道HttpServletRequest是在执行doService方法之前,也就是具体的业务逻辑前进行设置的,然后在执行完业务逻辑或者抛出异常时重置RequestContextHolder移除当前的HttpServletRequest。

写本文的目的主要是记录下这个RequestContextHolder的使用,以及希望以后能在业务代码中巧用该工具让自己的代码更加简洁优雅。

ThreadLocal在Spring中的应用相关推荐

  1. (转)Spring中ThreadLocal的认识

    我们知道Spring通过各种DAO模板类降低了开发者使用各种数据持久技术的难度.这些模板类都是线程安全的,也就是说,多个DAO可以复用同一个模板实例而不会发生冲突.我们使用模板类访问底层数据,根据持久 ...

  2. Spring 中的bean 是线程安全的吗?

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 作者:myseries cnblogs.com/myser ...

  3. 一文读懂Spring中的AOP机制

    一.前言 这一篇我们来说一下 Spring 中的 AOP 机制,为啥说完注解的原理然后又要说 AOP 机制呢? 1.标记日志打印的自定义注解 @Target({ElementType.METHOD}) ...

  4. Spring中的Controller ,Service,Dao是不是线程安全的?

    作者:myseries cnblogs.com/myseries/p/11729800.html 结论:不是线程安全的 Spring容器中的Bean是否线程安全,容器本身并没有提供Bean的线程安全策 ...

  5. spring中那些让你爱不释手的代码技巧

    紧接上文<spring中这些能升华代码的技巧,可能会让你爱不释手>.本文继续总结我认为spring中还不错的知识点,希望对您有所帮助. 一. @Conditional的强大之处 不知道你们 ...

  6. spring中这些能升华代码的技巧,可能会让你爱不释手

    前言 最近越来越多的读者认可我的文章,还是件挺让人高兴的事情.有些读者私信我说希望后面多分享spring方面的文章,这样能够在实际工作中派上用场.正好我对spring源码有过一定的研究,并结合我这几年 ...

  7. 【Spring源码】Spring中的AOP底层原理分析

    AOP中的几个概念 Advisor 和 Advice Advice,我们通常都会把他翻译为通知,其实很不好理解,其实他还有另外一个意思,就是"建议",我觉得把Advice理解为&q ...

  8. (转)Spring中Singleton模式的线程安全

    不知道哪里的文章,总结性还是比较好的.但是代码凌乱,有的还没有图.如果找到原文了可以进行替换! spring中的单例 spring中管理的bean实例默认情况下是单例的[sigleton类型],就还有 ...

  9. Spring事务专题(四)Spring中事务的使用、抽象机制及模拟Spring事务实现

    前言 本专题大纲如下: 事务专题大纲 「对于专题大纲我又做了调整哈,主要是希望专题的内容能够更丰富,更加详细」,本来是想在源码分析的文章中附带讲一讲事务使用中的问题,这两天想了想还是单独写一篇并作为事 ...

最新文章

  1. 华云数字实名认证图片_华云数据与安宁完成产品兼容互认证 携手推出安宁安全邮件系统联合解决方案...
  2. 人类首张脑电波连接全图问世
  3. 一张照片就能生成3D模型,GAN和自动编码器碰撞出奇迹,苏黎世联邦理工学院出品...
  4. python升级pip在哪儿打开_Linux下升级python和安装pip的详解
  5. spring 配置只读事务_只读副本和Spring Data第3部分:配置两个实体管理器
  6. c++ max函数_「C/C++」函数:定义、调用、参数传递
  7. chm 转 html 带索引,chm 的项目文件中包含创建 chm 文件所需的HTML文件信息、目录表文件信息、索引文件信息、窗口属 - 试题答案网问答...
  8. 随机游走问题的神奇应用(二)
  9. java实现浏览器ui中的收藏夹_Java实现简单的图片浏览器
  10. 高并发的常见策略--大型web项目
  11. mysql db for python_Python使用MySQLdb for Python操作数据库教程
  12. 变量声明和定义及extern 转载
  13. python爬虫实例1:获取一个网页的列表数据
  14. 华硕 PRIME H410M-K + i5-10400F 黑苹果 EFI文件
  15. 杨辉三角形JAVA代码
  16. java drawline_JAVA drawLine()方法怎么用?
  17. Oracle轻量级客户端下载,Oracle轻量级客户端使用,Oracle轻量级客户端配置,本地同时安装服务器端和客户端,并实现plsql developer连接
  18. gma 教程 | 气候气象 | 计算标准化降水指数(SPI)
  19. 亲测码支付第三方支付源码、完美对接日主题系列网站
  20. shal+php,学习笔记---PHP中几种加密算法(MD5,shal,base64_encode等)

热门文章

  1. Bless You Autocorrect!
  2. Pascal's Triangle Leetcode Java and C++
  3. WORD 粘贴代码 不检查语法
  4. 洛谷 P1800 software_NOI导刊2010提高(06)(二分答案+DP检验)
  5. Oracle一个中文汉字占用几个字节
  6. 浣溪沙·过杜甫草堂有感
  7. 通向架构师的道路(第十天)之Axis2 Web Service(一)
  8. sql语句转化为分页查询的一种实现
  9. WinForm加载外部类库项目的集成开发模式
  10. 通用权限管理系统组件 (GPM - General Permissions Manager) 中实现按部门组织机构设置权限...