前几天看了领导写的一段代码,在Controller中注入了HttpServletRequest,形式如下所示:
当时看到了这一段代码,首先想到的是AutowiredRequestController是一个singleton的bean,HttpServletRequest是一个变化的共享变量,每个请求对象都是不一样的,这样写不会有线程安全问题吗?带着疑问去翻了翻SpringMVC的源码,结论是:不会有线程安全问题!!!不会有线程安全问题!!!!不会有线程安全问题!!!下面我们来分析一下:

之前也说过会调用AbstractApplicationContext中的refresh方法进行Bean的组装初始化的过程,在refresh()这个方法中会调用:postProcessBeanFactory()这个方法,设置一些需要预先处理的Bean。而XmlWebApplicationContext继承了AbstractRefreshableWebApplicationContext,AbstractRefreshableWebApplicationContext中重写了postProcessBeanFactory这个方法,我们去AbstractRefreshableWebApplicationContext这个类中看一下这个方法的内容:
WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext);这个方法是我们需要重点分析的类,我进去看一下:
在这个类中注册了Web开发相关的三个Scope:RequestScope、SessionScope和全局的SessionScope以及一个ServletContextScope。接下来了注册了几个实现了ObjectFactory的Bean。RequestObjectFactory、RequestObjectFactory、SessionObjectFactory、WebRequestObjectFactory。这几个类是在Controller中注入Request和Response的关键。我们先记住RequestObjectFactory这个类。这几个类被放到了resolvableDependencies中。resolvableDependencies这个Map中存放的是在Autowire时所要使用到的bean的类型和对应的Object。
下面我们去看一下在Controller中注入Request的一个过程。首先看一下相应的一些调用链:
这个调用链比较长,调用链的执行过程也很复杂,这里先不过多展开的,我们直接进入到关键代码中。先进入到DefaultListableBeanFactory的findAutowireCandidates这个方法中,看一些关键代码:
在上面的代码中我们看到了resolvableDependencies这个属性。当注入HttpServletRequest的时候,requiredType值是HttpServletRequest.class,我们还记得在resolvableDependencies中放入了ServletRequest.class这个key。所以 if (autowiringType.isAssignableFrom(requiredType)) 这个判断会返回true,接着会取得RequestObjectFactory这个对象。接着会调用AutowireUtils.resolveAutowiringValue这个方法进一步的解析Autowire的值。我们进到这个方法中进行看一下:
在这个方法中会先判断上一步取得的autowireValue是不是可以实例化为ObjectFactory 对象。上一步取得的autowireValue是RequestObjectFactory。我们看一下RequestObjectFactory这个类的内容:
很明显的实现了ObjectFactory这个接口。接着RequestObjectFactory 是不能实例化为HttpServletRequest对象的。所以会进入到循环体中。接着进一步的判断上一步取得的autowireValue的值能不能被序列化,以及requiredType是不是可以接口,从上面的分析可以看出这个条件也是成立的。所以最终生成的autowireValue的值是一个JDK动态代理生成的对象。InvocationHandler的实现类为:ObjectFactoryDelegatingInvocationHandler。所以我们在Controller层中所注入的HttpServletRequest其实是一个JDK动态代理生成的对象。这个是很关键的一个点!!!当我们在Controller中调用的时候:
会进入到ObjectFactoryDelegatingInvocationHandler的invoke方法中。我们去看一下:
这里对equals方法、hashcode方法、toString方法进行了特殊处理。其他的方法则是直接执行method.invoke的方法。第一个参数为所调用的对象。是通过调用this.objectFactory.getObject()来获取的。objectFactory,通过我们上面的分析我们知道它是RequestObjectFactory这个对象。所以我们去RequestObjectFactory这个类中看一下objectFactory这个方法:
在上面的代码中调用了currentRequestAttributes().getRequest()。我们接着去currentRequestAttributes()这个方法中看一下:
看到这里时,你是不是会有一种恍然大悟的感觉呢?((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest();这种方式不也是我们经常在代码中获取HttpServletRequest对象的方式吗?我在进一步的分析一下。看一下RequestContextHolder.currentRequestAttributes()这个方法:
通过getRequestAttributes()这个方法来获取RequestAttributes对象。我们进入到getRequestAttributes()这个方法中继续看一下:
发现是通过requestAttributesHolder或者inheritableRequestAttributesHolder的get()方法,来获取的RequestAttributes对象。那么requestAttributesHolder和inheritableRequestAttributesHolder是什么呢?
两个ThreadLocal的对象!!!到这里总算是一切都真相大白了!!!每次都是从ThreadLocal对象中取的值,那还能不是线程安全的吗?这里在多说一句,什么时候往这两个ThreadLocal中放入值的呢?请接着往下看:org.springframework.web.servlet.FrameworkServlet#processRequest这个方法中有这样的几行代码:
initContextHolders这个方法的代码如下:
RequestContextHolder.setRequestAttributes这个方法的内容如下:
不需要再多说了。
这里总结一下:Controller层中所注入的HttpServletReuqest的实现类为JDK动态代理生成的一个代理类,从Request中获取值的时候是从ThreadLocal中得到的对象中的值。