问题:

我们知道在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进行存储到当时线程,反射调用的时分获取从当时线程。

预备

SpringMVC源码剖析之自动注入Request,为什么可行?

1、先从依靠注入视点来看,能够看到注入给userController的是一个署理目标,经过jdk proxy进行署理的request目标注入到了userController中(依靠注入交由AutowireAnnotationBeanPostProcessor处理)

SpringMVC源码剖析之自动注入Request,为什么可行?
经过AutowireAnnotationBeanPostProcessor实现依靠注入
SpringMVC源码剖析之自动注入Request,为什么可行?
SpringMVC源码剖析之自动注入Request,为什么可行?
获取候选的Bean进行注入
SpringMVC源码剖析之自动注入Request,为什么可行?
jdk proxy 创立署理目标,并指定了invocationHandler(调度处理器)
SpringMVC源码剖析之自动注入Request,为什么可行?

SpringMVC源码剖析之自动注入Request,为什么可行?
特点赋值依靠注入之后,能够看到userController中注入了署理目标request
SpringMVC源码剖析之自动注入Request,为什么可行?

2、调用目标handler办法的时分,署理目标request,经过invoke进行调用。终究经过method.invoke 进行反射调用。调用需要实在request目标,实在的request目标,从RequestContextHolder获取,RequestContextHolder经过ThreadLocal从当时线程中获取,而Thread中的变量属于当时线程所有,是线程安全的

SpringMVC源码剖析之自动注入Request,为什么可行?

SpringMVC源码剖析之自动注入Request,为什么可行?

SpringMVC源码剖析之自动注入Request,为什么可行?
SpringMVC源码剖析之自动注入Request,为什么可行?
SpringMVC源码剖析之自动注入Request,为什么可行?

requestAttributesHolder 是一个TheadLocal

SpringMVC源码剖析之自动注入Request,为什么可行?

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放入当时线程中

SpringMVC源码剖析之自动注入Request,为什么可行?

总结

SpringMVC源码剖析之自动注入Request,为什么可行?