提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
目录
前言
我们在使用SpringMvc处理请求的时候,可以在controller层或者service层注入HttpServletRequest对象,controller是一个单例对象,在容器初始化的过程中会对其进行属性注入,然而httprequest是随着每次请求的变化而变化的,所以基本不可能在初始化的时候得到。本篇文章主要探究spring对注入request对象的处理。
一、测试用例
测试用的案例比较简单,就是在controller中注入了request和response对象,并获取请求url;
@Controller
@RequestMapping("/demo")
public class DemoController {
@Autowired
HttpServletRequest request;
@Autowired
HttpServletResponse response;
@RequestMapping(value = "/testRequest",method = RequestMethod.GET)
@ResponseBody
public String testRequest() {
response.addHeader("header","header");
return request.getRequestURL().toString();
}
}
二、源码分析
1.request属性注入过程
代码通过分析源码得知,在注入request对象的时候,是在resolvableDependencies中找到了RequestObjectFactory类;
protected Map<String, Object> findAutowireCandidates(
@Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {
for (Map.Entry<Class<?>, Object> classObjectEntry : this.resolvableDependencies.entrySet()) {
// 得到当前Bean的类型
Class<?> autowiringType = classObjectEntry.getKey();
if (autowiringType.isAssignableFrom(requiredType)) {
// 获取缓存中的值
Object autowiringValue = classObjectEntry.getValue();
// 这里会生成一个Bean的名字,放到缓存中的是没有Bean的名字的
autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);
if (requiredType.isInstance(autowiringValue)) {
result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);
break;
}
}
}
return result;
}
在此处生成了代理对象,在调用代理对象的方法时,通过ObjectFactoryDelegatingInvocationHandler的invoke方法得知,实际上是调用上述objectFactory的方法。
public static Object resolveAutowiringValue(Object autowiringValue, Class<?> requiredType) {
if (autowiringValue instanceof ObjectFactory && !requiredType.isInstance(autowiringValue)) {
ObjectFactory<?> factory = (ObjectFactory<?>) autowiringValue;
if (autowiringValue instanceof Serializable && requiredType.isInterface()) {
//关键代码,在这里动态代理创建对象
autowiringValue = Proxy.newProxyInstance(requiredType.getClassLoader(),
new Class<?>[] {requiredType}, new ObjectFactoryDelegatingInvocationHandler(factory));
}
else {
return factory.getObject();
}
}
return autowiringValue;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
try {
return method.invoke(this.objectFactory.getObject(), args);
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
2.RequestObjectFactory
这里我们先考虑下ResponseObjectFactory,RequestObjectFactory是如何注入的,在容器启动的过程中,在AbstractRefreshableWebApplicationContext的postProcessBeanFactory方法中,会将web请求相关的objectFactory类加入到容器中,准确的说是resolvableDependencies中。
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
//BeanPostProcessor 的实现类,使用 ServletConfig 或者 ServletContext 类构造的 类;
beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext, this.servletConfig));
//实现该接口的类 希望 得到 ServletContext 的通知 ,即可以实时获取 ServletContext
beanFactory.ignoreDependencyInterface(ServletContextAware.class);
//实现该接口的类 希望 得到 ServletConfig的通知 ,即可以实时获取 ServletConfig;
beanFactory.ignoreDependencyInterface(ServletConfigAware.class);
//注册 请求域,session 域,应用域 以及 bean 工厂注册,便于在controller中直接获取
WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext);
WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, this.servletContext, this.servletConfig);
}
public static void registerWebApplicationScopes(ConfigurableListableBeanFactory beanFactory,
@Nullable ServletContext sc) {
//registerResolvableDependency 该方法的主要作用就是指定该类型接口,如果外部要注入该类型接口的对象,则会注入我们指定的对象,而不会去管其他接口实现类
beanFactory.registerResolvableDependency(ServletRequest.class, new RequestObjectFactory());
beanFactory.registerResolvableDependency(ServletResponse.class, new ResponseObjectFactory());
beanFactory.registerResolvableDependency(HttpSession.class, new SessionObjectFactory());
beanFactory.registerResolvableDependency(WebRequest.class, new WebRequestObjectFactory());
}
通过RequestObjectFactory的源码可以看到,getObject方法是从本地线程RequestContextHolder中获取,所以不同的请求处理线程会获取到不同的request对象。
private static class RequestObjectFactory implements ObjectFactory<ServletRequest>, Serializable {
@Override
public ServletRequest getObject() {
return currentRequestAttributes().getRequest();
}
}
private static ServletRequestAttributes currentRequestAttributes() {
RequestAttributes requestAttr = RequestContextHolder.currentRequestAttributes();
return (ServletRequestAttributes) requestAttr;
}
在每次请求的过程中,FrameworkServlet中的processRequest方法会调用initContextHolders方法,将request对象存储到requestAttributesHolder中,从而实现在线程本地中获取request。
private void initContextHolders(HttpServletRequest request,
@Nullable LocaleContext localeContext, @Nullable RequestAttributes requestAttributes) {
if (localeContext != null) {
LocaleContextHolder.setLocaleContext(localeContext, this.threadContextInheritable);
}
if (requestAttributes != null) {
RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
}
}
总结
在这里可以总结一下,首先在容器初始化的时候,会通过postProcessBeanFactory方法,在 resolvableDependencies中添加RequestObjectFactory对象,随后在controller对象的属性注入的时候,注入代理对象;在调用代理对象的方法时,会直接调用RequestObjectFactory的getObject方法对应对象的方法,而getObject方法会将当前请求request在threadlocal中取出;至于当前请求是在initContextHolders中将request放入到threadlocal中。