@Autowired依赖注入HttpServletRequest 和 HttpServletResponse 原理

场景

通常来说依赖注入的对象一旦创建完成后就不会在改变,因为Spring的默认行为创建的都是单例对象。HttpServletRequestHttpServletResponse不是单例对象,它们都是根据HTTP请求进行创建的,一个HTTP请求对象一个request和response。但是通过@Autowired可以实现每当一个请求进来时使用的都是当前的request或response对象,

演示代码如下

@RestController
@RequestMapping("/test")
public void TestController {
    @Autowired
    private HttpServletRequest request;
    
    @GetMapping("/request")
    public String test() {
        String queryString = request.getQueryString();
    }
}

实现原理

实际上,通过@Autowire注入的HttpServletRequest只是一个代理对象,其内部会实时获取当前请求对应的真正的HttpServletRequest对象。

在上面的TestController中,框架在进行依赖注入HttpServletRequest 时,会去IOC容器中查找类型为HttpServletRequest的Bean,

首先会调用DefaultListableBeanFactory#doResolveDependency(DependencyDescriptor, String, Set<String>, TypeConverter)进行依赖的解析,最终会调用到DefaultListableBeanFactory#findAutowireCandidates(String, Class<?>, DependencyDescriptor), 该方法主要是从依赖注入源中查找对应类型的Bean,源码如下:

// 存放用于依赖注入的Bean
private final Map<Class<?>, Object> resolvableDependencies = new ConcurrentHashMap<>(16);

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);
        for (Class<?> autowiringType : this.resolvableDependencies.keySet()) {
            if (autowiringType.isAssignableFrom(requiredType)) {
                // 从依赖注入源查找对应类型的Bean
                Object autowiringValue = this.resolvableDependencies.get(autowiringType);
                
                // 如果有必要的话,为autowiringValue创建代理对象
                autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);
                if (requiredType.isInstance(autowiringValue)) {
                    result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);
                    break;
                }
            }
        }
    
    // 省略其他代码...
    return result;
}

值得注意的是resolvableDependencies中存放的Bean只用于依赖注入,不用于依赖查找

this.resolvableDependencies.get(autowiringType);这段代码中会获取到一个类型为RequestObjectFactory的Bean,该Bean实现了ObjectFactory 接口,属于WebApplicationContextUtils的内部类,源码如下

    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;
}

通过代码可以看出来RequestObjectFactory 的作用就是实时从ThreadLocal中获取当前请求对应的request对象,RequestContextHolder 内部使用的就是ThreadLocal

在获取到RequestObjectFactory 对象后,AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType) 这段代码中会为其创建一个代理对象,源码如下

// AutowireUtils#resolveAutowiringValue
public static Object resolveAutowiringValue(Object autowiringValue, Class<?> requiredType) {
        if (autowiringValue instanceof ObjectFactory && !requiredType.isInstance(autowiringValue)) {
            // 此时 factory = RequestObjectFactory
            ObjectFactory<?> factory = (ObjectFactory<?>) autowiringValue;
            if (autowiringValue instanceof Serializable && requiredType.isInterface()) {
                
                // 创建代理对象, requiredType = HttpServletRequest.class
                // 并将RequestObjectFactory传入ObjectFactoryDelegatingInvocationHandler中
                autowiringValue = Proxy.newProxyInstance(requiredType.getClassLoader(),
                        new Class<?>[] {requiredType}, new ObjectFactoryDelegatingInvocationHandler(factory));
            }
            else {
                return factory.getObject();
            }
        }
        return autowiringValue;
    }


private static class ObjectFactoryDelegatingInvocationHandler implements InvocationHandler, Serializable {

        private final ObjectFactory<?> objectFactory;

        public ObjectFactoryDelegatingInvocationHandler(ObjectFactory<?> objectFactory) {
            this.objectFactory = objectFactory;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            String methodName = method.getName();
            if (methodName.equals("equals")) {
                // Only consider equal when proxies are identical.
                return (proxy == args[0]);
            }
            else if (methodName.equals("hashCode")) {
                // Use hashCode of proxy.
                return System.identityHashCode(proxy);
            }
            else if (methodName.equals("toString")) {
                return this.objectFactory.toString();
            }
            
            // 在代理类中,每次调用HttpServetRequest中的方法都会去RequestObjectFactory中获取线程本            // 地变量中的HttpServletRequest对象,并通过反射调用其对应的方法
            try {
                return method.invoke(this.objectFactory.getObject(), args);
            }
            catch (InvocationTargetException ex) {
                throw ex.getTargetException();
            }
        }

从源码中可以看出在示例代码TestController中调用request.getQueryString(),会直接进入到ObjectFactoryDelegatingInvocationHandlerinvoke()方法中,然后通过反射的方式调用真正的HttpServletRequest对象中的方法

RequestObjectFactory 和 RequestAttributes 初始化时机

RequestObjectFactory

在容器启动阶段,会有一个BeanFactory后置处理阶段,其中会调用AbstractApplicatioContext#postProcessBeanFactory(ConfigurableListableBeanFactory) 方法,其中一个子类,AnnotationConfigReactiveWebServerApplicationContext对其进行了实现,其中会调用到WebApplicationContextUtils#registerWebApplicationScopes(ConfigurableListableBeanFactory, ServletContext)方法,源码如下

public static void registerWebApplicationScopes(ConfigurableListableBeanFactory beanFactory,
            @Nullable 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);
        }

        // 添加依赖注入Bean的来源
        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);
        }
}

// DefaultListableBeanFactory#registerResolvableDependency
public void registerResolvableDependency(Class<?> dependencyType, @Nullable Object autowiredValue) {
        Assert.notNull(dependencyType, "Dependency type must not be null");
        if (autowiredValue != null) {
            if (!(autowiredValue instanceof ObjectFactory || dependencyType.isInstance(autowiredValue))) {
                throw new IllegalArgumentException("Value [" + autowiredValue +
                        "] does not implement specified dependency type [" + dependencyType.getName() + "]");
            }
            // 添加进依赖注入源
            this.resolvableDependencies.put(dependencyType, autowiredValue);
        }
}

在上面代码中会将RequestObjectFactory和其他实现了ObjectFactory接口的对象添加到依赖注入源中,到这一步也就和上面获取requset对象的过程对应起来了

RequestAttributes

RequestAttributes 在Spring中的多处代码中都有使用到,这里列举两个Web场景下常用的两处

RequestContextFilter中

第一个在过滤器RequestContextFilter中,会把当前的HttpServletRequestHttpServletResponse都放入ThreadLocal中,过滤器代码如下

public class RequestContextFilter extends OncePerRequestFilter {
    
    // 省略类中其他代码...
    
    // 该方法属于OncePerRequestFilter的模板方法,
    // 会在OncePerRequestFilter#doFilter(ServletRequest, ServletResponse, FilterChain)中被调用
    protected void doFilterInternal(
            HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {

        // 创建RequestAttributes的子类对象`ServletRequestAttributes`
        ServletRequestAttributes attributes = new ServletRequestAttributes(request, response);
        // 初始化RequestContextHolder
        initContextHolders(request, attributes);

        try {
            filterChain.doFilter(request, response);
        }
        finally {
            resetContextHolders();
            if (logger.isDebugEnabled()) {
                logger.debug("Cleared thread-bound request context: " + request);
            }
            attributes.requestCompleted();
        }
    }
    
    
    private void initContextHolders(HttpServletRequest request, ServletRequestAttributes requestAttributes) {
        LocaleContextHolder.setLocale(request.getLocale(), this.threadContextInheritable);
        // 将RequestAttributes 放入ThreadLocal中
        RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
    }
    
}

RequestContextListener中

RequestContextListener 实现了Servlet中的监听器ServletRequestListener, 当事件发生,会将requset对象放入ThreadLocal

public class RequestContextListener implements ServletRequestListener {

    private static final String REQUEST_ATTRIBUTES_ATTRIBUTE =
            RequestContextListener.class.getName() + ".REQUEST_ATTRIBUTES";


    @Override
    public void requestInitialized(ServletRequestEvent requestEvent) {
        if (!(requestEvent.getServletRequest() instanceof HttpServletRequest)) {
            throw new IllegalArgumentException(
                    "Request is not an HttpServletRequest: " + requestEvent.getServletRequest());
        }
        HttpServletRequest request = (HttpServletRequest) requestEvent.getServletRequest();
        ServletRequestAttributes attributes = new ServletRequestAttributes(request);
        request.setAttribute(REQUEST_ATTRIBUTES_ATTRIBUTE, attributes);
        LocaleContextHolder.setLocale(request.getLocale());
        RequestContextHolder.setRequestAttributes(attributes);
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,802评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,109评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,683评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,458评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,452评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,505评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,901评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,550评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,763评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,556评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,629评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,330评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,898评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,897评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,140评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,807评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,339评论 2 342