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获取用户信息等
最新文章
- Windows server 2012远程桌面会话主机和远程桌面授权,server2012
- velocity.js用法整理1
- 一张图让你看懂各开源License[转]
- Tip和菜单的实现方式
- codevs1688 求逆序对
- Android端通过HttpURLConnection上传文件到服务器
- 【Visual Studio2010】创建XAML分析插件
- 利用<;JavascriptSerializer类>; 进行Json对象的序列化和反序列化
- 编译pure-ftpd时提示错误Your MySQL client libraries aren&#39;t properly installed
- 64位CentOS 6.4下安装wine(32位)
- Linux下终端利器tmux(转)
- wemall app商城源码机器人检测
- 一步一步深入理解Dijkstra算法
- 使用svn与maven管理的项目导入Eclipse,但是与本地svn客户端关联不上?
- 麒麟子Cocos Creator实用技巧
- Ocelot + Consul实践
- ES6常用语法(下)
- 团队项目管理:Github项目误删恢复记录
- Mongo如何导出 CSV文件
- 用NBU无法还原数据库到ASM磁盘