先说结论,在Controller中注入Request是线程安全的。

以下是解释:

我们先来看看这两者有什么不同

在controller注入成员变量request

可以看到注入的是一个代理对象

写在方法参数上

可以看到是一个tomcat原生的RequestFacade对象

那接下来我们看看在controller注入成员变量request是怎么实现的?

可以看到,我们找到

org.springframework.beans.factory.support.AutowireUtils.ObjectFactoryDelegatingInvocationHandler

@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 {
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();
}
}
}

  

可以看到 当代理对象被调用的时候,会先调用这个handler里面的

ObjectFactory.getObject()方法获取相关对象 在执行该对象的相关方法

AbstractApplicationContext抽象类是ApplicationContext的抽象实现类,里面的refresh()方法定义了Spring容器在加载配置文件后的各项处理工作。

其中定义了一个模板方法

postProcessBeanFactory(beanFactory);

AbstractRefreshableWebApplicationContext覆盖了这个方法

	@Override
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext, this.servletConfig));
beanFactory.ignoreDependencyInterface(ServletContextAware.class);
beanFactory.ignoreDependencyInterface(ServletConfigAware.class);
WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext);
WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, this.servletContext, this.servletConfig);
}

  

其中在这个方法里面

WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext);

这里设置了一些特殊的bean的scope,比如request,session,并设置了一些比较特殊的注入值

public static void registerWebApplicationScopes(ConfigurableListableBeanFactory beanFactory, ServletContext sc) {
beanFactory.registerScope(WebApplicationContext.SCOPE_REQUEST, new RequestScope());
beanFactory.registerScope(WebApplicationContext.SCOPE_SESSION, new SessionScope(false));
beanFactory.registerScope(WebApplicationContext.SCOPE_GLOBAL_SESSION, new SessionScope(true));
if (sc != null) {
ServletContextScope appScope = new ServletContextScope(sc);
beanFactory.registerScope(WebApplicationContext.SCOPE_APPLICATION, appScope);
// Register as ServletContext attribute, for ContextCleanupListener to detect it.
sc.setAttribute(ServletContextScope.class.getName(), appScope);
}
//这里对这些特殊值注入相关的ObjectFactory
beanFactory.registerResolvableDependency(ServletRequest.class, new RequestObjectFactory());
beanFactory.registerResolvableDependency(ServletResponse.class, new ResponseObjectFactory());
beanFactory.registerResolvableDependency(HttpSession.class, new SessionObjectFactory());
beanFactory.registerResolvableDependency(WebRequest.class, new WebRequestObjectFactory());
if (jsfPresent) {
FacesDependencyRegistrar.registerFacesDependencies(beanFactory);
}
}

  

而上面的代码。当我们调用Controller成员变量的request的时候,都会通过

RequestObjectFactory的getobject获取到真正的对象 并执行他的方法

接着去看看 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;
}

    发现是从RequestContextHolder中获取的

继续点击RequestContextHolder

最后发现其实request是从threadLocal中取...

那问题又来了 request 是什么时候放到threadlocal 里面的?

是在Springmvc的dispatcherServlet的父类FrameworkServlet里操作的.

@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
} /**
* Delegate POST requests to {@link #processRequest}.
* @see #doService
*/
@Override
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}

  不管你是doGet还是doPost还是doXXX方法都是委托processRequest方法去做的.

我们再看看 processRequest() 这个方法

其中调用了initContextHolders方法将request放到ThreadLocal里面去的

总结;

1这样子使用是可以的,不会有线程安全问题,

2基于此我们封装一个CommonController 把一些获取数据的方法可以封装在里面。比如通过token获取用户信息等

 

SpringMVC Controller中注入Request成员域和在方法中定义中HttpServletRequest有啥区别的更多相关文章

  1. 【转】在SpringMVC Controller中注入Request成员域

    原文链接:https://www.cnblogs.com/abcwt112/p/7777258.html 原文作者:abcwt112 主题 在工作中遇到1个问题....我们定义了一个Controlle ...

  2. 在SpringMVC Controller中注入Request成员域

    主题 在工作中遇到1个问题....我们定义了一个Controller基类,所有Springmvc自定义的controller都继承它....在它内部定义一个@Autowired HttpServlet ...

  3. springMVC controller间跳转 重定向 传递参数的方法

    springMVC controller间跳转 重定向 传递参数的方法 spring MVC框架controller间跳转,需重定向.有几种情况:不带参数跳转,带参数拼接url形式跳转,带参数不拼接参 ...

  4. SpringMVC在Controller层中注入request的坑

    记一次为了节省代码没有在方法体中声明HttpServletRequest,而用autowire直接注入所钻的坑 结论 给心急的人. 直接在Controller的成员变量上使用@Autowire声明Ht ...

  5. SpringMvc4中获取request、response对象的方法

    springMVC4中获取request和response对象有以下两种简单易用的方法: 1.在control层获取 在control层中获取HttpServletRequest和HttpServle ...

  6. 在Struts2的Action中获得request response session几种方法

    转载自~ 在Struts2中,从Action中取得request,session的对象进行应用是开发中的必需步骤,那么如何从Action中取得这些对象呢?Struts2为我们提供了四种方式.分别为se ...

  7. js中__proto__, property, prototype, 对象自身属性方法和原型中的属性方法的区别

    __proto__: 这个属性是实例对象的属性,每个实例对象都有一个__proto__属性,这个属性指向实例化该实例的构造函数的原型对象(prototype). proterty:这个方法是对象的属性 ...

  8. java类中的static成员变量和static方法简单介绍,持续补充

    一.静态成员变量 1.属于整个类而不是某个对象实例,所以可以直接通过类名和对象名去调用. 2.静态成员属于整个类,当系统第一次使用该类时,就会为其分配内存空间直到该类被卸载才会进行资源回收 二.静态方 ...

  9. java中获取request与response对象的方法

    Java 获取Request,Response对象方法   第一种.参数 @RequestMapping("/test") @ResponseBody public void sa ...

随机推荐

  1. windows server 2008 r2 x64 enterprise service pack1中aspjpeg.dll安装

    官网地址:http://persits.com/ 64位:http://www.persits.com/aspjpeg64.exe sn:lptn9dQO1enAePAXB2wFaCZawYWzfm0 ...

  2. linux git 保存用户名和密码

    一.通过文件方式 1.在~/下, touch创建文件 .git-credentials, 用vim编辑此文件,输入内容格式: touch .git-credentials vim .git-crede ...

  3. es的返回数据结构

    ES即简单又复杂,你可以快速的实现全文检索,又需要了解复杂的REST API.本篇就通过一些简单的搜索命令,帮助你理解ES的相关应用.虽然不能让你理解ES的原理设计,但是可以帮助你理解ES,探寻更多的 ...

  4. windows配置远程桌面连接到ubuntu

    最近在用nodejs开发项目,同时也在做一些区块链相关的工作,公司给配的办公电脑着实不错,都是自家品牌的工作站,市场价都是15K+了.但是在win10上装虚拟机,还是不太顺畅的.因为公司电脑是五年到期 ...

  5. linux下误删目录文件后恢复神器extundelete

    原文链接:https://blog.51cto.com/wzlinux/2052835 参考:https://blog.csdn.net/cwg_1992/article/details/463100 ...

  6. C++Primer第五版——习题答案详解(三)

    习题答案目录:https://www.cnblogs.com/Mered1th/p/10485695.html 第4章 表达式 练习4.10 while(cin>>i&&i ...

  7. Java笔试面试题整理第八波

    转载至:http://blog.csdn.net/shakespeare001/article/details/51388516 作者:山代王(开心阳) 本系列整理Java相关的笔试面试知识点,其他几 ...

  8. day44前端开发1之html基础

    web前端开发1一.前端三剑客之html 1.为标记语言,是非编程语言 2.自身不具备逻辑,遇到负责重复操作只能全部手写(Ctrl+C > V) 3.组成:标签, 指令, 实体 标签:由< ...

  9. 【395】yield 和 yield from

    yield:生成器 yield from:将生成器 yield 的内容相当于逐一在 yield 一般 参考:Python 3: Using "yield from" in Gene ...

  10. Java并发辅助类的使用

    目录 1.概述 2.CountdownLatch 2-1.构造方法 2-2.重要方法 2-3.使用示例 3.CyclicBarrier 3-1.构造方法 3-2.使用示例 4.Semaphore 4- ...