0
点赞
收藏
分享

微信扫一扫

【Spring源码三千问】Spring自动注入ServletRequest的原理是什么?

前言

我们可以通过 @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 可以免费下载源码测试工程…

举报

相关推荐

Spring动态代理原理是什么?

0 条评论