前言
我们可以通过 @Autowired 来注入 ServletRequest、ServletResponse、HttpSession 等。
做为一个有技术敏感性的程序猿,我们肯定会有疑问:
这些 bean 是在哪里定义的呢?
每次请求时 ServletRequest 不是都不一样吗?为什么还可以注入 ServletRequest、HttpServletRequest?
下面我们就来探究一下底层的原理!
版本约定
Spring 5.3.9 (通过 SpringBoot 2.5.3 间接引入的依赖)
正文
我们可以通过一个简单的例子来调试源码的方式来找到答案。
使用 @Resource 注入一个 ServletRequest,然后在 ResourceElement#getResourceToInject()
方法上打上断点。
观察堆栈就可以看出,ServletRequest 类型的依赖在解析时,是在 resolvableDependencies
中解析出来的。
resolvableDependencies 所在的源码处:DefaultListableBeanFactory#findAutowireCandidates()
为什么选用 @Resource 来注入?
答:@Resource 在 Spring 源码中使用的频率很低,通过这种方式注入的话,我们可以很方便的打断点,观察 bean 注入的情况。
这是一个很实用的小技巧,建议收藏!
也就是说 ServletRequest 类型被 Spring 注册成了一个 resolvable Dependency
(可解析的依赖)
注册 resolvable Dependency 的源码如下:
// WebApplicationContextUtils#registerWebApplicationScopes()
public static void registerWebApplicationScopes(ConfigurableListableBeanFactory beanFactory, ServletContext sc) {
beanFactory.registerScope(WebApplicationContext.SCOPE_REQUEST, new RequestScope());
beanFactory.registerScope(WebApplicationContext.SCOPE_SESSION, new SessionScope());
if (sc != null) {
ServletContextScope appScope = new ServletContextScope(sc);
beanFactory.registerScope(WebApplicationContext.SCOPE_APPLICATION, appScope);
// Register as ServletContext attribute, for ContextCleanupListener to detect it.
sc.setAttribute(ServletContextScope.class.getName(), appScope);
}
// 注册可解析的依赖,可以通过 @Autowired, @Resource 进行注入
beanFactory.registerResolvableDependency(ServletRequest.class, new RequestObjectFactory());
beanFactory.registerResolvableDependency(ServletResponse.class, new ResponseObjectFactory());
beanFactory.registerResolvableDependency(HttpSession.class, new SessionObjectFactory());
beanFactory.registerResolvableDependency(WebRequest.class, new WebRequestObjectFactory());
if (jsfPresent) {
FacesDependencyRegistrar.registerFacesDependencies(beanFactory);
}
}
registerResolvableDependency 的相关知识请戳 【Spring源码三千问】BeanDefinition注册、Bean注册、Dependency注册有什么区别?
我们可以观察到,最终会通过 DefaultListableBeanFactory#findAutowireCandidates()
找到待注入的依赖。
findAutowireCandidates() 方法会先从 resolvableDependencies
中获取到 ServletRequest,然后通过 ObjectFactory
获取到真正的依赖。
怎么样做到不同的 request 注入获取到不同的 ServletRequest bean 呢?
试想一下:如果依赖只是单纯的通过 ObjectFactory 获取到的话,那么 bean 初始化完成之后,依赖的 bean 对象就确定下来了。
那么针对不同的 request,怎么获取到相应 request 中的请求参数呢?都是同一个 ServletRequest 对象的话,怎么进行隔离?
仔细观察 DefaultListableBeanFactory#findAutowireCandidates()
会发现,它会调用 AutowireUtils#resolveAutowiringValue()
来解析 resolvable Dependency 依赖。
// AutowireUtils#resolveAutowiringValue()
public static Object resolveAutowiringValue(Object autowiringValue, Class<?> requiredType) {
// Spring 解析出来的依赖类型是 A(A 是 ObjectFactory 类型),但是真正需要依赖注入的类型是 B,那么就表示需要通过 ObjectFactory 去获取真正的依赖对象。
if (autowiringValue instanceof ObjectFactory && !requiredType.isInstance(autowiringValue)) {
ObjectFactory<?> factory = (ObjectFactory<?>) autowiringValue;
// 注入的是接口类型,并且是实现了 Serializable 的,就会产生一个 JDK proxy
if (autowiringValue instanceof Serializable && requiredType.isInterface()) {
autowiringValue = Proxy.newProxyInstance(requiredType.getClassLoader(),
new Class<?>[] {requiredType}, new ObjectFactoryDelegatingInvocationHandler(factory));
} else {
return factory.getObject();
}
}
return autowiringValue;
}
Spring 解析出来的依赖类型是 A(A 是 ObjectFactory 类型),但是真正需要依赖注入的类型是 B,那么就表示需要通过 ObjectFactory 去获取真正的依赖对象。
并且,针对依赖注入的类型是接口类型且实现了 Serializable,会将 ObjectFactory 包装成一个 JDK 代理对象进行注入。
private static class ObjectFactoryDelegatingInvocationHandler implements InvocationHandler, Serializable {
private final ObjectFactory<?> objectFactory;
ObjectFactoryDelegatingInvocationHandler(ObjectFactory<?> objectFactory) {
this.objectFactory = objectFactory;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
switch (method.getName()) {
case "equals":
// Only consider equal when proxies are identical.
return (proxy == args[0]);
case "hashCode":
// Use hashCode of proxy.
return System.identityHashCode(proxy);
case "toString":
return this.objectFactory.toString();
}
try {
// 除了 equals、hashCode、toString 方法外,全部路由到 objectFactory.getObject() 产生的对象
return method.invoke(this.objectFactory.getObject(), args);
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
}
也就是说,最终注入的 ServletRequest 对象是一个代理对象,ObjectFactoryDelegatingInvocationHandler
每次都会通过 RequestObjectFactory#getObject()
来获取对象来使用。
疑问:@Scope("request") 与 ServletRequest 有什么关系?
通过观察 ServletRequest 的一些特性,会发现它跟 @Scope("request") 标记的对象很相似。它们有什么区别和联系呢?
它们内部的实现方式不同:
@Scope("request") 内部是通过注册 request 类型的 scope,最后通过 RequestScope#get() 来实现的。
而 ServletRequest 是通过注册 resolvable Dependency
,最后通过 RequestObjectFactory 来实现的。
ServletRequest 是一个 resolvable Dependency (可解析的依赖),刚好它的特性跟 @Scope("request") 很像。
但是并不是每一个 resolvable Dependency 都具有这样的特性的。
小结
我们能通过 @Autowired 自动注入 ServletRequest,是因为 Spring 将 ServletRequest 注册成了一个 resolvable Dependency(可解析的依赖)。
ServletRequest 的特性和 @Scope("request") 类似,但是底层实现各不相同。
了解更多源码知识,请点击视频讲解:
SpringIoC源码由浅入深 : https://edu.51cto.com/course/30243.html
如果本文对你有所帮助,欢迎点赞收藏!
有关 Spring 源码方面的问题欢迎留言一起交流...
公众号后台回复:下载IoC 或者 下载AOP 可以免费下载源码测试工程…