原文网址:SpringMVC原理--Controller线程安全_IT利刃出鞘的博客-CSDN博客
简介
本文介绍SpringBoot中controller的线程安全性。
主要分析注入request时的线程安全。结论是:注入request是线程安全的。
问题复现
代码
package com.example.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
@RestController
@RequestMapping
public class HelloController {
@Autowired
private HttpServletRequest request;
@GetMapping("/test1")
public String test1() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(request);
return request.toString();
}
}
线程安全性
线程安全(不是测出来的,是看代码得出的)
分析:在Spring中,Controller的scope是singleton(单例),也就是说在整个web系统中,只有一个TestController;但是其注入的request却是线程安全的。
测试:接口中睡眠,模拟多个请求同时处理
使用上边“代码示例”中的代码
Postman开两个窗口,都访问:http://localhost:8080/test1
结果:(这个结果很奇怪,只能去追寻源码。)
Current HttpServletRequest
Current HttpServletRequest
源码分析
简介
这么写是线程安全的,原因如下:
- 在Controller中注入的request是动态代理对象,ObjectFactoryDelegatingInvocationHandler的实例。
- 当我们调用成员域request的方法的时候其实是调用了objectFactory的getObject()对象的相关方法。这里的objectFactory是RequestObjectFactory.
- 请求刚进入springmvc的dispatcherServlet时会把request相关对象设置到RequestContextHolder的threadlocal中去。
- RequestObjectFactory的getObject其实是从RequestContextHolder的threadlocal中去取值的.
打断点
request的代理类:AutowireUtils$ObjectFactoryDelegatingInvocationHandler
在图中可以看出,request实际上是一个代理:代理的实现参见AutowireUtils的内部类ObjectFactoryDelegatingInvocationHandler:
/**
* Reflective InvocationHandler for lazy access to the current target object.
*/
@SuppressWarnings("serial")
private static class ObjectFactoryDelegatingInvocationHandler implements InvocationHandler, Serializable {
private final ObjectFactory<?> objectFactory;
public ObjectFactoryDelegatingInvocationHandler(ObjectFactory<?> objectFactory) {
this.objectFactory = objectFactory;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// ……省略无关代码
try {
return method.invoke(this.objectFactory.getObject(), args); // 代理实现核心代码
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
}
当我们调用request的方法method时,实际上是调用了由objectFactory.getObject()生成的对象的method方法;objectFactory.getObject()生成的对象才是真正的request对象。
工厂类:WebApplicationContextUtils$RequestObjectFactory
继续观察上图,发现objectFactory的类型为WebApplicationContextUtils的内部类RequestObjectFactory:
/**
* Factory that exposes the current request object on demand.
*/
@SuppressWarnings("serial")
private static class RequestObjectFactory implements ObjectFactory<ServletRequest>, Serializable {
@Override
public ServletRequest getObject() {
return currentRequestAttributes().getRequest();
}
@Override
public String toString() {
return "Current HttpServletRequest";
}
}
其中,要获得request对象需要先调用currentRequestAttributes()方法获得RequestAttributes对象,该方法的实现如下:
/**
* Return the current RequestAttributes instance as ServletRequestAttributes.
*/
private static ServletRequestAttributes currentRequestAttributes() {
RequestAttributes requestAttr = RequestContextHolder.currentRequestAttributes();
if (!(requestAttr instanceof ServletRequestAttributes)) {
throw new IllegalStateException("Current request is not a servlet request");
}
return (ServletRequestAttributes) requestAttr;
}
生成RequestAttributes对象的核心代码在类RequestContextHolder中,其中相关代码如下(省略了该类中的无关代码):
public abstract class RequestContextHolder {
public static RequestAttributes currentRequestAttributes() throws IllegalStateException {
RequestAttributes attributes = getRequestAttributes();
// 此处省略不相关逻辑…………
return attributes;
}
public static RequestAttributes getRequestAttributes() {
RequestAttributes attributes = requestAttributesHolder.get();
if (attributes == null) {
attributes = inheritableRequestAttributesHolder.get();
}
return attributes;
}
private static final ThreadLocal<RequestAttributes> requestAttributesHolder =
new NamedThreadLocal<RequestAttributes>("Request attributes");
private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder =
new NamedInheritableThreadLocal<RequestAttributes>("Request context");
}
通过这段代码可以看出,生成的RequestAttributes对象是线程局部变量(ThreadLocal),因此request对象也是线程局部变量;这就保证了request对象的线程安全性。
另外需要注意的是,注入的必须是接口,才能保证是线程安全的:
Autowired#resolveAutowiringValue:
public static Object resolveAutowiringValue(Object autowiringValue, Class<?> requiredType) {
// 如果注入到的值为ObjectFactory类型(并且不是requiredType实例),就猪呢比下面的代理吧~~~
if (autowiringValue instanceof ObjectFactory && !requiredType.isInstance(autowiringValue)) {
ObjectFactory<?> factory = (ObjectFactory<?>) autowiringValue;
// 若 autowiringValue 实现了Serializable 接口
// 且requiredType接口(HttpServletRequest是接口,继承自ServletRequest)
// 所以要注意,只有注入接口才是线程安全的,若注入实现类,线程就是不安全的(因为无法创建代理了)
if (autowiringValue instanceof Serializable && requiredType.isInterface()) {
// 创建出来的代理对象,才是最终要被注入进去的值====
autowiringValue = Proxy.newProxyInstance(requiredType.getClassLoader(),
new Class<?>[] {requiredType}, new ObjectFactoryDelegatingInvocationHandler(factory));
}
else {
return factory.getObject();
}
}
return autowiringValue;
}