@Autowired依赖注入HttpServletRequest

2021-08-13  本文已影响0人  繁书_

场景

通常来说依赖注入的对象一旦创建完成后就不会在改变,因为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);
    }
}
上一篇下一篇

猜你喜欢

热点阅读