为什么HttpServletRequest能够autowired注入?为什么多请求不混乱?

问题的引入

最近看代码里发现有将HttpServletRequest 直接通过@Autowired注入。我有点想不明白,注入的默认是单例,这样不会出问题吗?为了研究明白为什么,我写了个简单的demo研究了下。

demo代码

以下是springmvc里的一个controller注入了一个httpServletRequest。
有个简单的get方法,将2个request里的header 同时打印出来,查看结果。

@RestController
public class SimpleController {


    //注入了一个httpServletRequest
    @Autowired
    HttpServletRequest autoRequest;
    
     @RequestMapping("/get")
    public String get(HttpServletRequest request){
        System.out.println("------------------------------------------");
        Enumeration<String> en=autoRequest.getHeaderNames();
        System.out.println(autoRequest);
        while(en.hasMoreElements()){
            String key;
            System.out.println((key=en.nextElement()) +" = "+autoRequest.getHeader(key));
        }
        System.out.println("=========================================");
        Enumeration<String> en2=request.getHeaderNames();
        System.out.println(request);
        while(en2.hasMoreElements()){
            String key;
            System.out.println((key=en2.nextElement()) +" = "+request.getHeader(key));
        }

        String uuid=(String) request.getSession().getAttribute("uuid");
        System.out.println(request.getSession().getId());
        return uuid==null?"none":uuid;
    }

}

2个浏览器分别call后打印出结果对比,发现打印的cookie会改变,打印出的结果并没有想象中autoRequest不变的结果。
带着疑问,第一个问题就是

HttpServletRequest 是怎么注入进去的?

由于知道,所有注入的的bean都是需要通过beanfactory的 getBean获取得到。于是断点AbstractBeanFactory.doGetBean然后通过条件断点,期望能够断到HttpServletRequest的获取的过程。

在这里插入图片描述
遗憾的是不能够获取到。于是换了思路,写了个after

@RestController
public class SimpleController {

    @Autowired
    HttpServletRequest autoRequest;
    @PostConstruct
    public void after(){
        //断点这里查看
        System.out.println(this.autoRequest);
    }
}

发现有个ObjectFactory,顾名思义是生产出 request的工厂类
在这里插入图片描述
进入到WebApplicatonContextUtils.java,发现这里放入了一个生产出HttpServletRequest的beanFactory
在这里插入图片描述

private static class RequestObjectFactory implements ObjectFactory<ServletRequest>, Serializable {

		@Override
		public ServletRequest getObject() {
			return currentRequestAttributes().getRequest();
		}

		@Override
		public String toString() {
			return "Current HttpServletRequest";
		}
	}

至此应该就能明白注入的地方了。那下个疑问,

@autowired request 是怎么找到这个RequestObjectFactory

我们知道 AutowiredAnnotationBeanPostProcessor 是用来处理bean里的属性的注入的. 是众多的BeanPostProcessor里的一种
在这里插入图片描述
AutowiredAnnotationBeanPostProcessor最后到DefaultListableBeanFactory.findAutowireCandidates

protected Map<String, Object> findAutowireCandidates(
			@Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {

		String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
				this, requiredType, true, descriptor.isEager());
		Map<String, Object> result = new LinkedHashMap<>(candidateNames.length);
   //重点在这里this.resolvableDependencies
   //之前beanFactory.registerResolvableDependency(ServletRequest.class, new RequestObjectFactory());
   //实际上就是this.resolvableDependencies.put(dependencyType, autowiredValue);
   
		for (Map.Entry<Class<?>, Object> classObjectEntry : this.resolvableDependencies.entrySet()) {
			Class<?> autowiringType = classObjectEntry.getKey();
			//循环到这里发现有个key的class 是ServletRequest的派生类			
			if (autowiringType.isAssignableFrom(requiredType)) {
			     //拿到autowiringValue ,就是RequestObjectFactory
				Object autowiringValue = classObjectEntry.getValue();
				//调用AutowireUtils.resolveAutowiringValue创建出一个对象
				autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);
				if (requiredType.isInstance(autowiringValue)) {
					result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);
					break;
				}
			}
		}
		for (String candidate : candidateNames) {
			if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {
				addCandidateEntry(result, candidate, descriptor, requiredType);
			}
		}
		if (result.isEmpty()) {
			boolean multiple = indicatesMultipleBeans(requiredType);
			// Consider fallback matches if the first pass failed to find anything...
			DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch();
			for (String candidate : candidateNames) {
				if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, fallbackDescriptor) &&
						(!multiple || getAutowireCandidateResolver().hasQualifier(descriptor))) {
					addCandidateEntry(result, candidate, descriptor, requiredType);
				}
			}
			if (result.isEmpty() && !multiple) {
				// Consider self references as a final pass...
				// but in the case of a dependency collection, not the very same bean itself.
				for (String candidate : candidateNames) {
					if (isSelfReference(beanName, candidate) &&
							(!(descriptor instanceof MultiElementDescriptor) || !beanName.equals(candidate)) &&
							isAutowireCandidate(candidate, fallbackDescriptor)) {
						addCandidateEntry(result, candidate, descriptor, requiredType);
					}
				}
			}
		}
		return result;
	}

结论就是
1.beanFactory.registerResolvableDependency(ServletRequest.class, new RequestObjectFactory()); 实际上就是往resolvableDependencies这个map里扔
2.AutowiredAnnotationBeanPostProcessor 处理的时候要去找一个ServletRequest,会在resolvableDependencies找,找到一个key为ServletRequest.class的类就是RequestObjectFactory

至此怎么找到 RequestObjectFactory 也明白了。
也到了快要揭开为什么多请求不混乱?

为什么多请求不混乱?

private static class RequestObjectFactory implements ObjectFactory<ServletRequest>, Serializable {

		@Override
		public ServletRequest getObject() {
			return currentRequestAttributes().getRequest();
		}

		@Override
		public String toString() {
			return "Current HttpServletRequest";
		}
	}
	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;
	}
public static RequestAttributes currentRequestAttributes() throws IllegalStateException {
		RequestAttributes attributes = getRequestAttributes();
		...
		return attributes;
	}
  //答案在这里,这个是一个ThreadLocal对象,也就是每个request的请求都会将封装好的RequestAttributes放到这个ThreadLocal里。
     private static final ThreadLocal<RequestAttributes> requestAttributesHolder =
			new NamedThreadLocal<>("Request attributes");

	 private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder =
			new NamedInheritableThreadLocal<>("Request context");
     public static RequestAttributes getRequestAttributes() {
		RequestAttributes attributes = requestAttributesHolder.get();
		if (attributes == null) {
			attributes = inheritableRequestAttributesHolder.get();
		}
		return attributes;
	}

既然是ThreadLocal一定就会有set的地方,很容易看到RequestContextHolder的setRequestAttributes方法,断点到这里,在请求一次。
在这里插入图片描述
可以看出请求过来会进入RequestContextFilter这个filter,这里会将request给设置到当前的线程里。

protected void doFilterInternal(
			HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {

		ServletRequestAttributes attributes = new ServletRequestAttributes(request, response);
		//将request设到当前的线程里
		initContextHolders(request, attributes);

		try {
		//继续走filterChain里的其它filter
			filterChain.doFilter(request, response);
		}
		finally {
		  //最后当请求返回后,需要将清除线程里的threadlocal
			resetContextHolders();
			if (logger.isTraceEnabled()) {
				logger.trace("Cleared thread-bound request context: " + request);
			}
			attributes.requestCompleted();
		}
	}

至此,完整的明白了HttpServletRequest能autowired注入,并且多请求不混乱。

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值