【轉】在SpringMVC Controller中注入Request成員域


原文鏈接:https://www.cnblogs.com/abcwt112/p/7777258.html

原文作者:abcwt112

主題

  在工作中遇到1個問題....我們定義了一個Controller基類,所有Springmvc自定義的controller都繼承它....在它內部定義一個@Autowired HttpServletRequest request;可不可以? 能不能從這個對象里取requestParamters和attributes? 多線程之間會不會影響?

 

思考

初次思考,我想這應該是不行的.為什么呢?

注入bean是在spring容器啟動的時候...request的實現類是在tomcat里(我使用的servlet容器是tomcat)....我又沒在spring的容器里配置這個bean.注入應該是失敗的...

退一步說,就算是成功了....那注入的也就是1個對象而已.每次servlet接受到請求都會重新生成1個request...這明顯和之前啟動的那個對象不同吧....怎么想都不可能成功...

 

如果確實是這樣的....那就沒有這篇文章了....后來實踐了一下..發現這個注入是可以的.使用起來取數據也沒任何問題....

 

其實我那個時候debug看了一下,基本就知道為什么可以取到數據了..但是我並不知道原理和Spring(Springmvc)的處理流程...所以現在研究了一下並記錄下來...

 

原理

首先給大家看一下在方法中注入request作為參數和在成員域中注入request的 注入的request對象之間的區別....

 

 

成員域注入的時候注入的是1個代理對象.是 AutowireUtils.ObjectFactoryDelegatingInvocationHandler的實例.

方法注入的就是一般tomcat原生的requestFacade對象.

所以這是不同的...

/**
     * Reflective InvocationHandler for lazy access to the current target object.
     */
    @SuppressWarnings("serial")
    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();
            }
            try {
                return method.invoke(this.objectFactory.getObject(), args);
            }
            catch (InvocationTargetException ex) {
                throw ex.getTargetException();
            }
        }
    }

 

當代理對象(就是成員域request)的大部分方法被調用的時候,ObjectFactoryDelegatingInvocationHandler會使用objectFactory獲取對象(原生request),再調用對象上的方法.

 

 

然后我們來看下XmlWebApplicationContext初始化到請求到進入controller里幾個對注入request成員域有影響的步驟.

 

refresh方法和postProcessBeanFactory方法

ApplicationContext的抽象實現類AbstractApplicationContext(基本是所有ac的父類)里定義了ac的refresh方法(包含了使用BeanFactory注入bean)的流程..

@Override
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // Prepare this context for refreshing.
            // 記錄開始wac開始初始化的時間,設置激活標記,servlet的相關param設置到env(之前做過1次),校驗env中必須的props
            prepareRefresh();

            // Tell the subclass to refresh the internal bean factory.
            // 將舊的BF里的bean刪掉,新建1個BF,設置部分屬性,加載XML配置文件
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            // Prepare the bean factory for use in this context.
            // 1.設置BF解析bean配置需要用到的一些對象比如env. 2.注冊一些BeanPostProcessor比如ApplicationContextAwareProcessor去設置Aware需要的對象
            // 3.忽略一些特定class注入的對象,設置一些特定class注入的對象為指定值
            // 4.將一些env中的properties map當做bean注冊到BF中
            prepareBeanFactory(beanFactory);

            try {
                // Allows post-processing of the bean factory in context subclasses.
                // 1.設置一個BeanPostProcess為ServletContextAware的實現類注入servlet相關對象
                // 2.在BF中增加requetsScope等Scope
                // 3.把servletContext,Config,ServletInitParams,ServletAttribute當做Bean注冊到BF中
                postProcessBeanFactory(beanFactory);

                // Invoke factory processors registered as beans in the context.
                // 初始化並調用BeanFactoryPostProcessor
                invokeBeanFactoryPostProcessors(beanFactory);

                // Register bean processors that intercept bean creation.
                // 注冊BeanPostProcessors並注冊到BF中去
                registerBeanPostProcessors(beanFactory);

                // Initialize message source for this context.
                initMessageSource();

                // Initialize event multicaster for this context.
                initApplicationEventMulticaster();

                // Initialize other special beans in specific context subclasses.
                onRefresh();

                // Check for listener beans and register them.
                registerListeners();

                // Instantiate all remaining (non-lazy-init) singletons.
                finishBeanFactoryInitialization(beanFactory);

                // Last step: publish corresponding event.
                finishRefresh();
            } catch (BeansException ex) {
                logger.warn("Exception encountered during context initialization - cancelling refresh attempt", ex);

                // Destroy already created singletons to avoid dangling resources.
                destroyBeans();

                // Reset 'active' flag.
                cancelRefresh(ex);

                // Propagate exception to caller.
                throw ex;
            }
        }
    }

 

 

其中有1個模板方法

postProcessBeanFactory(beanFactory);

這個方法允許AbstractApplicationContext的子類覆蓋它並實現對BF的定制(這個時候bean的defination路徑已經指定了,但是bean還沒加載).

 

AbstractRefreshableWebApplicationContext覆蓋了這個方法

/**
     * Register request/session scopes, a {@link ServletContextAwareProcessor}, etc.
     * 1.設置一個BeanPostProcess為ServletContextAware的實現類注入servlet相關對象
     * 2.在BF中增加requetsScope等Scope
     * 3.把servletContext,Config,ServletInitParams,ServletAttribute當做Bean注冊到BF中
     *
     */
    @Override
    protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        // 設置一個BeanPostProcess為ServletContextAware的實現類注入servlet相關對象
        beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext, this.servletConfig));
        beanFactory.ignoreDependencyInterface(ServletContextAware.class);
        beanFactory.ignoreDependencyInterface(ServletConfigAware.class);

        // 在BF中增加requetsScope等Scope
        WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext);
        // 把servletContext,Config,ServletInitParams,ServletAttribute當做Bean注冊到BF中
        WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, this.servletContext, this.servletConfig);
    }

 

其中有一步

WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext);

這里設置了一些特殊的bean的scope,比如request,session,globalSession,application.(當然這個不是我這篇文章的主題.)

同時設置了一些特殊的autowired 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());

 

ServletRequest的實現類(比如HttpServletRequest)被指定使用RequestObjectFactory注入.

 

RequestObjectFactory

RequestObjectFactory就是1個ObjectFactory就是前面ObjectFactoryDelegatingInvocationHandler里的ObjectFactory.所以在成員域request對象上調用方法其實就是通過RequestObjectFactory獲取對象再調用方法.

    /**
     * Factory that exposes the current request object on demand.
     */
    @SuppressWarnings("serial")
    private static class RequestObjectFactory implements ObjectFactory<ServletRequest>, Serializable {

        @Override
        public ServletRequest getObject() {
            return currentRequestAttributes().getRequest();
        }

        @Override
        public String toString() {
            return "Current HttpServletRequest";
        }
    }
    /**
     * Return the current RequestAttributes instance as ServletRequestAttributes.
     * @see RequestContextHolder#currentRequestAttributes()
     */
    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的getObject方法很簡單,就是調用靜態方法

RequestContextHolder.currentRequestAttributes().getRequest()

 

RequestContextHolder

 

    public static RequestAttributes currentRequestAttributes() throws IllegalStateException {
        RequestAttributes attributes = getRequestAttributes();
        if (attributes == null) {
            if (jsfPresent) {
                attributes = FacesRequestAttributesFactory.getFacesRequestAttributes();
            }
            if (attributes == null) {
                throw new IllegalStateException("No thread-bound request found: " +
                        "Are you referring to request attributes outside of an actual web request, " +
                        "or processing a request outside of the originally receiving thread? " +
                        "If you are actually operating within a web request and still receive this message, " +
                        "your code is probably running outside of DispatcherServlet/DispatcherPortlet: " +
                        "In this case, use RequestContextListener or RequestContextFilter to expose the current request.");
            }
        }
        return attributes;
    }
    /**
     * Return the RequestAttributes currently bound to the thread.
     * @return the RequestAttributes currently bound to the thread,
     * or {@code null} if none bound
     */
    public static RequestAttributes getRequestAttributes() {
        RequestAttributes attributes = requestAttributesHolder.get();
        if (attributes == null) {
            attributes = inheritableRequestAttributesHolder.get();
        }
        return attributes;
    }
    private static final ThreadLocal<RequestAttributes> requestAttributesHolder =
            new NamedThreadLocal<RequestAttributes>("Request attributes");

    private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder =
            new NamedInheritableThreadLocal<RequestAttributes>("Request context");

 

上面是一些關鍵方法

所以最終其實request是從threadlocal中取...

 

FrameworkServlet

 那么request是什么時候設置到threadlocal中去的呢?

是在Springmvc的dispatcherServlet的父類FrameworkServlet里操作的.

    @Override
    protected final void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        processRequest(request, response);
    }


    @Override
    protected final void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        processRequest(request, response);
    }

 

不管你是doGet還是doPost還是doXXX方法都是委托processRequest方法去做的.

    protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        long startTime = System.currentTimeMillis();
        Throwable failureCause = null;

        LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
        LocaleContext localeContext = buildLocaleContext(request);

        RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
        asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());

        initContextHolders(request, localeContext, requestAttributes); try {
            doService(request, response);
        }
        catch (ServletException ex) {
            failureCause = ex;
            throw ex;
        }
        catch (IOException ex) {
            failureCause = ex;
            throw ex;
        }
        catch (Throwable ex) {
            failureCause = ex;
            throw new NestedServletException("Request processing failed", ex);
        }

        finally {
            resetContextHolders(request, previousLocaleContext, previousAttributes);
            if (requestAttributes != null) {
                requestAttributes.requestCompleted();
            }

            if (logger.isDebugEnabled()) {
                if (failureCause != null) {
                    this.logger.debug("Could not complete request", failureCause);
                }
                else {
                    if (asyncManager.isConcurrentHandlingStarted()) {
                        logger.debug("Leaving response open for concurrent processing");
                    }
                    else {
                        this.logger.debug("Successfully completed request");
                    }
                }
            }

            publishRequestHandledEvent(request, response, startTime, failureCause);
        }
    }

 

其中調用了

initContextHolders(request, localeContext, requestAttributes);

 

    private void initContextHolders(
            HttpServletRequest request, LocaleContext localeContext, RequestAttributes requestAttributes) {

        if (localeContext != null) {
            LocaleContextHolder.setLocaleContext(localeContext, this.threadContextInheritable);
        }
        if (requestAttributes != null) {
            RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
        }
        if (logger.isTraceEnabled()) {
            logger.trace("Bound request context to thread: " + request);
        }
    }

 

就是在這里設置到RequestContextHolder的threadlocal中去的...

 

 

小結

1.在controller中注入的request是jdk動態代理對象,ObjectFactoryDelegatingInvocationHandler的實例.當我們調用成員域request的方法的時候其實是調用了objectFactory的getObject()對象的相關方法.這里的objectFactory是RequestObjectFactory.

2.RequestObjectFactory的getObject其實是從RequestContextHolder的threadlocal中去取值的.

3.請求剛進入springmvc的dispatcherServlet的時候會把request相關對象設置到RequestContextHolder的threadlocal中去.

 

對於原文的補充

原文沒有詳細描述成員域request注入的具體過程,補充如下:

ApplicationContext的初始化調用過程如下:

在refresh方法被調用后,會調用org.springframework.beans.factory.support.DefaultListableBeanFactory#findAutowireCandidates

    protected Map<String, Object> findAutowireCandidates(
            String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {

        String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                this, requiredType, true, descriptor.isEager());
        Map<String, Object> result = new LinkedHashMap<String, Object>(candidateNames.length);
        for (Class<?> autowiringType : this.resolvableDependencies.keySet()) {
            if (autowiringType.isAssignableFrom(requiredType)) {
                Object autowiringValue = this.resolvableDependencies.get(autowiringType);
                autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);
                if (requiredType.isInstance(autowiringValue)) {
                    result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);
                    break;
                }
            }
        }
        for (String candidateName : candidateNames) {
            if (!isSelfReference(beanName, candidateName) && isAutowireCandidate(candidateName, descriptor)) {
                result.put(candidateName, getBean(candidateName));
            }
        }
        if (result.isEmpty()) {
            DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch();
            for (String candidateName : candidateNames) {
                if (!candidateName.equals(beanName) && isAutowireCandidate(candidateName, fallbackDescriptor)) {
                    result.put(candidateName, getBean(candidateName));
                }
            }
        }
        return result;
    }

 

 該方法內部會調用:

org.springframework.beans.factory.support.AutowireUtils#resolveAutowiringValue

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

 

 可以看到,如果需要注入的對象是接口類型的話,則為其創建代理對象。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM