SpringBoot 注入实例变量Request线程安全吗?

环境:SpringBoot2.3.9.RELEASE


  1. 测试Controller类
@RestController
@RequestMapping("/message")
public class MessageController {
	
	@Resource
	private HttpServletRequest request ;
	
	@PostMapping("/resolver")
	public Object resolver(@RequestBody Users user) {
		System.out.println(request) ;
		return user ;
	}
}
  1. Debug模式调试代码查看Request到底是撒

SpringBoot 注入实例变量Request线程安全吗?

 

通过调试,request对象实际注入的是一个代理对象
ObjectFactoryDelegatingInvocationHandler,查看该类源码:

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 {
			String methodName = method.getName();
			if (methodName.equals("equals")) {
				// Only consider equal when proxies are identical.
				return (proxy == args[0]);
			}
			else if (methodName.equals("hashCode")) {
				// Use hashCode of proxy.
				return System.identityHashCode(proxy);
			}
			else if (methodName.equals("toString")) {
				return this.objectFactory.toString();
			}
			try {
				return method.invoke(this.objectFactory.getObject(), args);
			}
			catch (InvocationTargetException ex) {
				throw ex.getTargetException();
			}
		}
	}

这是通过JDK的动态代理来实现的,该类有个属性ObjectFactory,通过名称也能也能猜出是一个对象工厂类,根据上面的调试也知道了,这里的objectFactory对象实际是RequestObjectFactory,通过这个类的.getObject()方法来获取真实的Request对象。

RequestObjectFactory源码:

@SuppressWarnings("serial")
	private static class RequestObjectFactory implements ObjectFactory<ServletRequest>, Serializable {

		@Override
		public ServletRequest getObject() {
			return currentRequestAttributes().getRequest();
		}

		@Override
		public String toString() {
			return "Current HttpServletRequest";
		}
	}

currentRequestAttributes()方法:

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

currentRequestAttributes()方法:

public static RequestAttributes currentRequestAttributes() throws IllegalStateException {
		RequestAttributes attributes = getRequestAttributes();
		if (attributes == null) {
			if (jsfPresent) {
				attributes = FacesRequestAttributesFactory.getFacesRequestAttributes();
			}
			if (attributes == null) {
				throw new IllegalStateException("No thread-bound request found: " +
						"Are you referring to request attributes outside of an actual web request, " +
						"or processing a request outside of the originally receiving thread? " +
						"If you are actually operating within a web request and still receive this message, " +
						"your code is probably running outside of DispatcherServlet: " +
						"In this case, use RequestContextListener or RequestContextFilter to expose the current request.");
			}
		}
		return attributes;
	}

getRequestAttributes()方法:

public static RequestAttributes getRequestAttributes() {
		RequestAttributes attributes = requestAttributesHolder.get();
		if (attributes == null) {
			attributes = inheritableRequestAttributesHolder.get();
		}
		return attributes;
	}

源码跟踪到这里也就知道了,真实的HttpServletRequest对象是被存入在ThreadLocal对象中,线程本地变量与线程绑定了,也就是在使用Request对象时,都会从ThreadLocal中获取。

接下来看看这个HttpServletRequest对象是什么时候存入ThreadLocal中的。


Servlet处理流程:请求----》service() ----》doXXX()

SpringMVC核心控制器类DispatcherServlet的父类FrameworkServlet

FrameworkServlet类中对应的doXXX方法都有一行processRequest方法调用

SpringBoot 注入实例变量Request线程安全吗?

 

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());

		initContextHolders(request, localeContext, requestAttributes);

		try {
			doService(request, response);
		}
		catch (ServletException | IOException ex) {
			failureCause = ex;
			throw ex;
		}
		catch (Throwable ex) {
			failureCause = ex;
			throw new NestedServletException("Request processing failed", ex);
		}

		finally {
			resetContextHolders(request, previousLocaleContext, previousAttributes);
			if (requestAttributes != null) {
				requestAttributes.requestCompleted();
			}
			logResult(request, response, failureCause, asyncManager);
			publishRequestHandledEvent(request, response, startTime, failureCause);
		}
	}

下面两行代码是来创建ServletRequestAttributes对象

RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

buildRequestAttributes()方法

protected ServletRequestAttributes buildRequestAttributes(HttpServletRequest request,
			@Nullable HttpServletResponse response, @Nullable RequestAttributes previousAttributes) {

		if (previousAttributes == null || previousAttributes instanceof ServletRequestAttributes) {
			return new ServletRequestAttributes(request, response);
		}
		else {
			return null;  // preserve the pre-bound RequestAttributes instance
		}
	}

在这里将request和response对象都存入到了ServletRequestAttributes对象中。

接着进入initContextHolders()方法

private void initContextHolders(HttpServletRequest request,
			@Nullable LocaleContext localeContext, @Nullable RequestAttributes requestAttributes) {

		if (localeContext != null) {
			LocaleContextHolder.setLocaleContext(localeContext, this.threadContextInheritable);
		}
		if (requestAttributes != null) {
			RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
		}
	}

进入
RequestContextHolder.setRequestAttributes(requestAttributes,
this.threadContextInheritable);这行代码

public static void setRequestAttributes(@Nullable RequestAttributes attributes, boolean inheritable) {
		if (attributes == null) {
			resetRequestAttributes();
		}
		else {
			if (inheritable) {
				inheritableRequestAttributesHolder.set(attributes);
				requestAttributesHolder.remove();
			}
			else {
				requestAttributesHolder.set(attributes);
				inheritableRequestAttributesHolder.remove();
			}
		}
	}

该方法就是将RequestAttributes(HttpServletRequest, HttpServletResponse)对象存入到ThreadLocal中。

到此也已经说明了,在Controller中注入HttpServletRequest和HttpServletResponse是安全的。


完毕!!!

SpringBoot 注入实例变量Request线程安全吗?

 

SpringBoot 注入实例变量Request线程安全吗?

 

SpringBoot 注入实例变量Request线程安全吗?

 

SpringBoot 注入实例变量Request线程安全吗?

 

SpringBoot 注入实例变量Request线程安全吗?

 

 

SpringBoot 注入实例变量Request线程安全吗?

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值