0
点赞
收藏
分享

微信扫一扫

SpringMVC原理--Controller线程安全

全栈顾问 2022-03-23 阅读 57



注入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

分析

其他网址

​​​​

简介

这么写是线程安全的,原因如下:

  1. 在Controller中注入的request是动态代理对象,ObjectFactoryDelegatingInvocationHandler的实例。
  1. 当我们调用成员域request的方法的时候其实是调用了objectFactory的getObject()对象的相关方法。这里的objectFactory是RequestObjectFactory.
  1. 请求刚进入springmvc的dispatcherServlet时会把request相关对象设置到RequestContextHolder的threadlocal中去。
  2. RequestObjectFactory的getObject其实是从RequestContextHolder的threadlocal中去取值的.

打断点

SpringMVC原理--Controller线程安全_线程安全

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;
}



举报

相关推荐

0 条评论