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

问题:

我们知道在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放入当前线程中 在这里插入图片描述

总结

在这里插入图片描述

猜你喜欢

转载自juejin.im/post/7114977761755660301
今日推荐