问题:
我们知道在SpringMVC中controller层可以通过Autowire自动注入Request到当前类来使用
如果看过Spring源码,IOC容器进行实例化bean的时候,一级缓存中存放的都是单例Bean
那么是否意味着Request,也是单例Bean,会不会出现线程安全?
如果使用过request,发现其并不会出现线程安全问题,那为什么单例Bean Request不会出现线程安全问题?
解决概述
request实际上是一个代理对象,因此依赖注入request是一个代理对象,当通过request.getXXX() 的时候,实际上走到了代理类的invoke方法上,而invoke方法本质上是通过method.invoke进行反射调用,需要一个真实的目标对象。
目标对象的获取是从RequestContextHolder中获取的,当前请求进来的时候,正经过过滤器filter,RequestContextHolder通过threadLocal将current request进行存储到当前线程,反射调用的时候获取从当前线程。
准备
1、先从依赖注入角度来看,可以看到注入给userController的是一个代理对象,通过jdk proxy进行代理的request对象注入到了userController中(依赖注入交由AutowireAnnotationBeanPostProcessor处理)
通过AutowireAnnotationBeanPostProcessor实现依赖注入
获取候选的Bean进行注入
jdk proxy 创建代理对象,并指定了invocationHandler(调度处理器)
属性赋值依赖注入之后,可以看到userController中注入了代理对象request
2、调用目标handler方法的时候,代理对象request,通过invoke进行调用。最终通过method.invoke 进行反射调用。调用需要真实request对象,真实的request对象,从RequestContextHolder获取,RequestContextHolder通过ThreadLocal从当前线程中获取,而Thread中的变量属于当前线程所有,是线程安全的
requestAttributesHolder 是一个TheadLocal
3、RequestContextHolder 什么时候将 请求request通过ThreadLocal 放入当前线程中的。可以看到当请求通过 OncePerRequestFilter的时候,存放request进当前线程
public class RequestContextFilter extends OncePerRequestFilter {
/**
* Returns "false" so that the filter may set up the request context in an
* error dispatch.
*/
@Override
protected boolean shouldNotFilterErrorDispatch() {
return false;
}
// 过滤器filter
@Override
protected void doFilterInternal(
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
ServletRequestAttributes attributes = new ServletRequestAttributes(request, response);
// 初始化contextHolder
initContextHolders(request, attributes);
try {
filterChain.doFilter(request, response);
}
finally {
resetContextHolders();
if (logger.isTraceEnabled()) {
logger.trace("Cleared thread-bound request context: " + request);
}
attributes.requestCompleted();
}
}
private void initContextHolders(HttpServletRequest request, ServletRequestAttributes requestAttributes) {
LocaleContextHolder.setLocale(request.getLocale(), this.threadContextInheritable);
// RequestContextHolder 设置 RequestAttributes
RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
if (logger.isTraceEnabled()) {
logger.trace("Bound request context to thread: " + request);
}
}
RequestContextHolder通过ThreadLocal 将request放入当前线程中