四时宝库

程序员的知识宝库

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到底是撒

通过调试,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方法调用

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是安全的。


完毕!!!

给个关注+转发,谢谢啊









Spring Retry重试框架的应用

spring data jpa 高级应用

Spring MVC 异步请求方式

Spring MVC 异常处理方式

Spring Cloud Sentinel 熔断降级

Spring Cloud Sentinel 流控限流

Spring Cloud Sentinel 基础配置

Spring Cloud Nacos 开启权限验证

Springboot整合openfeign使用详解

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言
    友情链接