在Web开发中,通常需要在服务层或工具类中检索http servlet对象。 一种方法是将HttpServletRequest作为方法的参数从控制器层拖放并传递,这有点辛苦,也不优雅。 另一个是RequestContextHolder,直接在需要的地方取HttpServletRequest即可。 使用代码如下。
htpservletrequestrequest=((servletrequestattributes ) requestcontextholder.getrequest attributes ).getrequest () ) 要理解上述为什么可以这样使用,需要理解两个问题。
为什么RequestContextHolder可以获取当前http servlet
HttpServletRequest是什么时候设置为RequestContextHolder的
关于第一个问题,熟悉趋势科技的人应该很容易看出这是趋势科技的应用。 这一类的原理在之前的博客(ThreadLocal原理)中进行了阐述,其实和之前的博客文末所述的UserContextHolder很相似。
第二个问题应该是spring-mvc的问题,它在运行spring-mvc时设置
分析源代码
首先,让我们看看RequestContextHolder的源代码。 源代码如下:
publicabstractclassrequestcontextholder { privatestaticfinalthreadlocalrequestattributestributestattributesholder=NND cfinalthreadlocalrequestattributesinheritablerequestattributesholder=newnamedinheritablethreadlocalrequestattributer=newnatributer estatattri publicstaticvoidresetrequestattributes (({ requestattributesholder.remove ); inheritablerequestattributesholder.remove (; } publicstaticvoidsetrequestattributes (requestattributes ) setrequestattributes (attributes,false ); //RequestAttributes对象包含在ThreadLocal中,而HttpServletRequest、HttpServletResponse等封装在RequestAttributes对象中。 在本例中,无论是不是封装在requestattributes对象中,都必须检索requestattributes对象,然后必须从requestattributes对象检索所需的http servlet rerebutes对象publicstaticvoidsetrequestattributes,booleaninhhutes (else ) if(inheritable ) inheritablerequestattributesholder.set ) requestAttributesHolder.remove (; } else { requestattributesholder.set (attributes ); inheritablerequestattributesholder.remove (; } } publicstaticrequestattributesgetrequestattributes ({ requestattributesattributes=requestattributesholder.get ) } if ()
butes = inheritableRequestAttributesHolder.get(); } return attributes; }}那么在spring-mvc中是怎么实现的呢,我们来简单分析的,想了解具体机制的可以去看看spring-mvc的源码。
我们看下FrameworkServlet这个类,也就是DispatcherServlet的父类,里面有个processRequest方法,根据方法名称我们也可以大概了解到这个是方法用于处理请求的。
简单看下源码,我们可以知道HttpServletRequest是在执行doService方法之前,也就是具体的业务逻辑前进行设置的,然后在执行完业务逻辑或者抛出异常时重置RequestContextHolder移除当前的HttpServletRequest。
【多线程】ThreadLocal原理
使用
在每个线程的内部有个数据结构为Map的threadLocals变量,以<ThreadLocal,Value>的形式保存着线程变量和其对应的值。
当使用set()方法时:
1.获取到当前线程的threadLocals,类型为Map
2.将这值放到这个Map结构的变量中,key为ThreadLocal对象,value为所有存放的值
当使用get()方法时:
1.获取到当前线程的threadLocals,类型为Map。
2.以ThreadLocal对象为Map的key获取到它的value值。
因为ThreadLocal对象作为Map的key,所以一个ThreadLocal对象只能存放一个值,当存放多个时,会将新值覆盖旧值。
数据结构:
public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t);//当前线程为入参,获取当前线程的threadLocals变量 if (map != null) //入参为this,也就是说key为ThreadLocal对象 map.set(this, value); else createMap(t, value); } public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t);//当前线程为入参,获取当前线程的threadLocals if (map != null) { //入参为this,也就是说key为ThreadLocal ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); } ThreadLocalMap getMap(Thread t) { return t.threadLocals;//threadLocals为线程的变量 } private Entry getEntry(ThreadLocal<?> key) { int i = key.threadLocalHashCode & (table.length - 1); Entry e = table[i]; if (e != null && e.get() == key) return e; else return getEntryAfterMiss(key, i, e);//避免内存泄漏,下文有提。 }https://www.jianshu.com/p/6bf1adb775e0