SpringMVC Controller中注入Request成员域和在方法中定义中HttpServletRequest有啥区别
先说结论,在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有啥区别的更多相关文章
- 【转】在SpringMVC Controller中注入Request成员域
		原文链接:https://www.cnblogs.com/abcwt112/p/7777258.html 原文作者:abcwt112 主题 在工作中遇到1个问题....我们定义了一个Controlle ... 
- 在SpringMVC Controller中注入Request成员域
		主题 在工作中遇到1个问题....我们定义了一个Controller基类,所有Springmvc自定义的controller都继承它....在它内部定义一个@Autowired HttpServlet ... 
- springMVC controller间跳转 重定向 传递参数的方法
		springMVC controller间跳转 重定向 传递参数的方法 spring MVC框架controller间跳转,需重定向.有几种情况:不带参数跳转,带参数拼接url形式跳转,带参数不拼接参 ... 
- SpringMVC在Controller层中注入request的坑
		记一次为了节省代码没有在方法体中声明HttpServletRequest,而用autowire直接注入所钻的坑 结论 给心急的人. 直接在Controller的成员变量上使用@Autowire声明Ht ... 
- SpringMvc4中获取request、response对象的方法
		springMVC4中获取request和response对象有以下两种简单易用的方法: 1.在control层获取 在control层中获取HttpServletRequest和HttpServle ... 
- 在Struts2的Action中获得request response session几种方法
		转载自~ 在Struts2中,从Action中取得request,session的对象进行应用是开发中的必需步骤,那么如何从Action中取得这些对象呢?Struts2为我们提供了四种方式.分别为se ... 
- js中__proto__, property, prototype, 对象自身属性方法和原型中的属性方法的区别
		__proto__: 这个属性是实例对象的属性,每个实例对象都有一个__proto__属性,这个属性指向实例化该实例的构造函数的原型对象(prototype). proterty:这个方法是对象的属性 ... 
- java类中的static成员变量和static方法简单介绍,持续补充
		一.静态成员变量 1.属于整个类而不是某个对象实例,所以可以直接通过类名和对象名去调用. 2.静态成员属于整个类,当系统第一次使用该类时,就会为其分配内存空间直到该类被卸载才会进行资源回收 二.静态方 ... 
- java中获取request与response对象的方法
		Java 获取Request,Response对象方法 第一种.参数 @RequestMapping("/test") @ResponseBody public void sa ... 
随机推荐
- <realsense D400>同步采集深度图和彩色图
			利用深度相机采集深度图和彩色图会面临一个问题,如何实现同步采集数据? 以下是我搜集到的两点方法: 1)高翔博士提到他的orbslam2教程有这么一步工作,具体目录为 example/RGBD/. (等 ... 
- NodeJS学习之win10安装与sublime配置
			Window 上安装Node.js Node.js安装包及源码下载地址为:https://nodejs.org/en/download/ 下载安装就行了,安装node会同时安装npm. sublime ... 
- Dubbo源码分析
			Dubbo源码分析1 Dubbo源码分析2 dubbo源码阅读:rpc请求处理流程(1) 架构设计:系统间通信(17)——服务治理与Dubbo 中篇(分析) 13. Dubbo原理解析-注册中心之Zo ... 
- android 开发 我的高德地图代码例子
			下载高德地图依赖库和相关注册方式,请查看高德开发者网站:http://lbs.amap.com/api/android-sdk/summary 点击打开链接 高德地图坐标拾取器:http://lbs ... 
- 201772020113李清华《面向对象程序设计(java)》第八周学习总结
			实验六 接口的定义与使用 实验时间 2018-10-18 1.实验目的与要求 (1) 掌握接口定义方法: (2) 掌握实现接口类的定义要求: (3) 掌握实现了接口类的使用要求: (4) 掌握程序回调 ... 
- Docekr 挂在卷之后访问目录时异常 cannot open directory '.': Permission denied 的解决办法
			1,原因,原因是CentOS7 中的安全模块 selinux 把权限禁掉了 2,解决办法如下 2.1,运行容器是加参数在 --privileged=true (个人认为这是最佳方式,推荐使用) 如 ... 
- 深度学习原理与框架-递归神经网络-RNN_exmaple(代码)  1.rnn.BasicLSTMCell(构造基本网络) 2.tf.nn.dynamic_rnn(执行rnn网络) 3.tf.expand_dim(增加输入数据的维度) 4.tf.tile(在某个维度上按照倍数进行平铺迭代) 5.tf.squeeze(去除维度上为1的维度)
			1. rnn.BasicLSTMCell(num_hidden) # 构造单层的lstm网络结构 参数说明:num_hidden表示隐藏层的个数 2.tf.nn.dynamic_rnn(cell, ... 
- 吴裕雄 python深度学习与实践(7)
			import cv2 import numpy as np img = np.mat(np.zeros((,))) cv2.imshow("test",img) cv2.waitK ... 
- cdnbest节点动态ip配置教程
			1.安装节点后,在未初始化里初始化节点,如下图操作,要选择动态ip(注:动态ip节点不支持添加辅ip) 服务器如果是动态ip,选择了动态ip选项,节点在自动更换了新的ip后,在节点列表里的ip和dns ... 
- 外网访问Vmware虚拟机中的某个服务(如http)
			如果主机是windowx NAT中隐藏的端口映射,说明一下环境,利用当然是VMnet8网络连接,在虚拟机中架设linux WEB服务器利用WEB默认80端口,IP为192.168.11.10,真实主机 ... 
