问题的引入
最近看代码里发现有将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注入,并且多请求不混乱。