大数跨境
0
0

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

SpringBoot 注入实例变量Request线程安全吗? Spring全家桶实战案例
2021-09-08
2
导读:在Springboot中Controller中你是如何使用Request,Response对象的?

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


完毕!!!

给个关注+转发,谢谢啊








【声明】内容源于网络
0
0
Spring全家桶实战案例
Java全栈开发,前端Vue2/3全家桶;Spring, SpringBoot 2/3, Spring Cloud各种实战案例及源码解读
内容 832
粉丝 0
Spring全家桶实战案例 Java全栈开发,前端Vue2/3全家桶;Spring, SpringBoot 2/3, Spring Cloud各种实战案例及源码解读
总阅读818
粉丝0
内容832