1. 程式人生 > 其它 >Spring中ObjectFactory的使用以及與FactoryBean 的區別&ServletRequest、ServlertResponse物件注入方式

Spring中ObjectFactory的使用以及與FactoryBean 的區別&ServletRequest、ServlertResponse物件注入方式

  在研究ServletRequest 以及 ServletResponse 等注入的過程中,發現還有一個ObjectFactory 物件,簡單研究下其使用以及原理。

1. ObjectFactory使用

  下面從兩種方式研究其使用, 一種是介面, 一種是類。

1. 主要程式碼

(1) 類的方式

package cn.xm.controller.system;

public class MyBean {

    public MyBean() {
        System.out.println("cn.xm.controller.system.MyBean.MyBean");
    }

    
public void print() { System.out.println("cn.xm.controller.system.MyBean.print"); } }

ObjectFactory

package cn.xm.controller.system;


import org.springframework.beans.BeansException;
import org.springframework.beans.factory.ObjectFactory;

import java.io.Serializable;

public class MyBeanObjectFactory implements
ObjectFactory<MyBean>, Serializable { public MyBeanObjectFactory() { System.out.println("cn.xm.controller.system.MyObjectFactory"); } @Override public MyBean getObject() throws BeansException { return new MyBean(); } }

(2) 介面方式

package cn.xm.controller.system;


public interface MyInterface { public void print(); }

實現類:

package cn.xm.controller.system;

public class MyInterfaceImpl implements MyInterface {

    public MyInterfaceImpl() {
        System.out.println("cn.xm.controller.system.MyInterfaceImpl.MyInterfaceImpl");
    }

    @Override
    public void print() {
        System.out.println("cn.xm.controller.system.MyInterfaceImpl");
    }
}

ObjectFactory

package cn.xm.controller.system;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.ObjectFactory;

import java.io.Serializable;

public class MyInterfaceObjectFactory implements ObjectFactory<MyInterface>, Serializable {

    public MyInterfaceObjectFactory() {
        System.out.println("cn.xm.controller.system.MyObjectFactory");
    }

    @Override
    public MyInterface getObject() throws BeansException {
        return new MyInterfaceImpl();
    }
}

2. 配置類

採用動態註冊bean 的方法,註冊到BeanFactory

package cn.xm.controller.system;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyBeanConfiguration implements BeanFactoryAware {

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        ConfigurableListableBeanFactory beanFactory1 = (ConfigurableListableBeanFactory) beanFactory;
        beanFactory1.registerResolvableDependency(MyBean.class, new MyBeanObjectFactory());
        beanFactory1.registerResolvableDependency(MyInterface.class, new MyInterfaceObjectFactory());
    }

}

3. 註冊到controller 檢視其注入的類

    @Autowired
    private MyBean myBean;

    @Autowired
    private MyInterface myInterface;

    @GetMapping("/test1")
    public JSONResultUtil<String> test1(ServletRequest request1, ServletResponse response2) {
        System.out.println("======");
        System.out.println(request.getParameter("param1"));
        System.out.println(request1.getParameter("param1"));
        myBean.print();
        myInterface.print();
        return new JSONResultUtil<>(true, "ok", "test1");
    }

4. 訪問檢視其列印資訊以及物件資訊

訪問: http://localhost:8088/test/test1?param1=t2

控制檯列印資訊如下:

======
t2
t2
cn.xm.controller.system.MyBean.print
cn.xm.controller.system.MyInterfaceImpl.MyInterfaceImpl
cn.xm.controller.system.MyInterfaceImpl

  可以看到對於介面注入的類呼叫的時候會呼叫getObject 方法建立物件,然後呼叫其方法; 對於類物件,會在啟動過程中IOC 過程就自動呼叫getObject 方法獲取物件。

2. ObjectFactory原理

  下面研究其原理。

1. ObjectFactory 和 FactoryBean 的區別

  前面瞭解到FactoryBean 是一個工廠物件,用於生產物件。我們注入FactoryBean 的時候實際會向容器中註冊兩個物件,一個是factoryBean 自身,一個是其getObject() 物件返回的物件。

  ObjectFactory 物件直白的意思就是物件工廠,其在Spring 可以動態註冊一些物件。原始碼如下:

package org.springframework.beans.factory;

import org.springframework.beans.BeansException;

/**
 * Defines a factory which can return an Object instance
 * (possibly shared or independent) when invoked.
 *
 * <p>This interface is typically used to encapsulate a generic factory which
 * returns a new instance (prototype) of some target object on each invocation.
 *
 * <p>This interface is similar to {@link FactoryBean}, but implementations
 * of the latter are normally meant to be defined as SPI instances in a
 * {@link BeanFactory}, while implementations of this class are normally meant
 * to be fed as an API to other beans (through injection). As such, the
 * {@code getObject()} method has different exception handling behavior.
 *
 * @author Colin Sampaleanu
 * @since 1.0.2
 * @see FactoryBean
 */
public interface ObjectFactory<T> {

    /**
     * Return an instance (possibly shared or independent)
     * of the object managed by this factory.
     * @return an instance of the bean (should never be {@code null})
     * @throws BeansException in case of creation errors
     */
    T getObject() throws BeansException;

}

2. 檢視其注入到上面物件的例項

   可以看到對於物件注入的直接是物件自身,上面可以看出對於MyBean 注入的是MyBean 物件自身; 對於介面注入的會注入一個JDK動態代理物件。

檢視org.springframework.beans.factory.support.AutowireUtils.ObjectFactoryDelegatingInvocationHandler 物件原始碼如下:

    /**
     * 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();
            }
        }
    }

  可以看到呼叫invoke 方法中, 如果呼叫的是其他方法會呼叫objectFactory.getObject() 方法,然後反射呼叫方法。

3. 檢視其呼叫原理

1. 動態註冊原理

org.springframework.beans.factory.support.DefaultListableBeanFactory#registerResolvableDependency

    /** Map from dependency type to corresponding autowired value */
    private final Map<Class<?>, Object> resolvableDependencies = new ConcurrentHashMap<Class<?>, Object>(16);
    
    @Override
    public void registerResolvableDependency(Class<?> dependencyType, 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);
        }
    }

  可以看到是將型別放入 resolvableDependencies 內部的一個併發Map 中。

2. 接下來研究其注入的過程

  以注入 cn.xm.controller.system.MyInterface 為例子。

1》 在ioc 過程中注入org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean 開始依賴注入,接下來注入過程中會呼叫到:org.springframework.beans.factory.support.DefaultListableBeanFactory#findAutowireCandidates

 1     protected Map<String, Object> findAutowireCandidates(
 2             String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {
 3 
 4         String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
 5                 this, requiredType, true, descriptor.isEager());
 6         Map<String, Object> result = new LinkedHashMap<String, Object>(candidateNames.length);
 7         for (Class<?> autowiringType : this.resolvableDependencies.keySet()) {
 8             if (autowiringType.isAssignableFrom(requiredType)) {
 9                 Object autowiringValue = this.resolvableDependencies.get(autowiringType);
10                 autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);
11                 if (requiredType.isInstance(autowiringValue)) {
12                     result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);
13                     break;
14                 }
15             }
16         }
17         for (String candidate : candidateNames) {
18             if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {
19                 addCandidateEntry(result, candidate, descriptor, requiredType);
20             }
21         }
22         if (result.isEmpty() && !indicatesMultipleBeans(requiredType)) {
23             // Consider fallback matches if the first pass failed to find anything...
24             DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch();
25             for (String candidate : candidateNames) {
26                 if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, fallbackDescriptor)) {
27                     addCandidateEntry(result, candidate, descriptor, requiredType);
28                 }
29             }
30             if (result.isEmpty()) {
31                 // Consider self references as a final pass...
32                 // but in the case of a dependency collection, not the very same bean itself.
33                 for (String candidate : candidateNames) {
34                     if (isSelfReference(beanName, candidate) &&
35                             (!(descriptor instanceof MultiElementDescriptor) || !beanName.equals(candidate)) &&
36                             isAutowireCandidate(candidate, fallbackDescriptor)) {
37                         addCandidateEntry(result, candidate, descriptor, requiredType);
38                     }
39                 }
40             }
41         }
42         return result;
43     }

  可以看到這裡在第7行的迴圈地方會先從自己的快取map裡面拿,當獲取到匹配的型別之後獲取到動態注入過程中的物件。上面獲取到的物件就是cn.xm.controller.system.MyInterfaceObjectFactory。

2》 接下來呼叫 org.springframework.beans.factory.support.AutowireUtils#resolveAutowiringValue 解析自動注入的物件

    /**
     * Resolve the given autowiring value against the given required type,
     * e.g. an {@link ObjectFactory} value to its actual object result.
     * @param autowiringValue the value to resolve
     * @param requiredType the type to assign the result to
     * @return the resolved value
     */
    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;
    }

  可以看到如果獲取到的物件 autowiringValue 是ObjectFactory, 並且該物件不是需要注入的物件的型別。會進入if 程式碼塊。

  然後if 程式碼塊裡面判斷如果對應的ObjectValue 實現了Serializable 介面, 並且需要的型別是介面的話,會用JDK的Proxy.newInstance 建立代理物件; 如果不滿足前面條件就直接呼叫factory.getObject() 獲取物件,然後直接注入物件自身。

  這裡也就驗證了上面,MyBean 注入的是MyBean 物件自身; 對於介面MyInterface 注入的會注入一個JDK動態代理物件。

3. 對於spring 注入的代理物件

  我們將斷點放到org.springframework.beans.factory.support.DefaultListableBeanFactory#registerResolvableDependency 可以檢視採用上面方式注入的主要有下面兩類物件:

1. org.springframework.context.support.AbstractApplicationContext#prepareBeanFactory 過程中注入一些物件如下:

        // BeanFactory interface not registered as resolvable type in a plain factory.
        // MessageSource registered (and found for autowiring) as a bean.
        beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
        beanFactory.registerResolvableDependency(ResourceLoader.class, this);
        beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
        beanFactory.registerResolvableDependency(ApplicationContext.class, this);

2. 容器啟動過程中會呼叫到: org.springframework.web.context.support.WebApplicationContextUtils#registerWebApplicationScopes(org.springframework.beans.factory.config.ConfigurableListableBeanFactory, javax.servlet.ServletContext) 注入相關request、response等物件

(1) 呼叫鏈如下: 可以看到是在Spring事件中觸發動態註冊

 (2) 核心原始碼如下:

    public static void registerWebApplicationScopes(ConfigurableListableBeanFactory beanFactory, ServletContext sc) {
        beanFactory.registerScope(WebApplicationContext.SCOPE_REQUEST, new RequestScope());
        beanFactory.registerScope(WebApplicationContext.SCOPE_SESSION, new SessionScope(false));
        beanFactory.registerScope(WebApplicationContext.SCOPE_GLOBAL_SESSION, new SessionScope(true));
        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);
        }

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

 4. ServletResquest、ServletResponse 注入原理

1. 採用下面方式注入ServletRequest和ServletResponse

    @Autowired
    private HttpServletResponse response;

    @Autowired
    private HttpServletRequest request;

    @GetMapping("/test1")
    public JSONResultUtil<String> test1(ServletRequest request1, ServletResponse response2) {
        System.out.println("======");
        System.out.println(request.getParameter("param1"));
        System.out.println(request1.getParameter("param1"));
        return new JSONResultUtil<>(true, "ok", "test1");
    }

  注入的實際上是一個代理物件,不需要考慮單例例項的問題, 因為注入的是代理物件,每次都會呼叫ObjectFactory.getObject() 獲取物件。我們注入的物件實際如下:

   可以看到Autowired 方式注入的物件實際是代理物件。 可以看到實際是org.springframework.beans.factory.support.AutowireUtils.ObjectFactoryDelegatingInvocationHandler, 該類原始碼如下:

    /**
     * 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();
            }
        }
    }
View Code

  以Request 為例子,實際會呼叫org.springframework.web.context.support.WebApplicationContextUtils.RequestObjectFactory 類獲取ServletRequest 物件。 org.springframework.web.context.support.WebApplicationContextUtils.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;
    }

  會呼叫 org.springframework.web.context.request.RequestContextHolder#currentRequestAttributes 獲取請求屬性:

    private static final ThreadLocal<RequestAttributes> requestAttributesHolder =
            new NamedThreadLocal<RequestAttributes>("Request attributes");

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

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

    public static RequestAttributes getRequestAttributes() {
        RequestAttributes attributes = requestAttributesHolder.get();
        if (attributes == null) {
            attributes = inheritableRequestAttributesHolder.get();
        }
        return attributes;
    }

  可以看到是從ThreadLocal 中獲取相關屬性物件,接下來研究其建立過程。

2. 屬性物件放入ThreadLocal 時機

(1) 方法org.springframework.web.context.request.RequestContextHolder#setRequestAttributes(org.springframework.web.context.request.RequestAttributes, boolean) 原始碼如下:

    /**
     * Bind the given RequestAttributes to the current thread.
     * @param attributes the RequestAttributes to expose,
     * or {@code null} to reset the thread-bound context
     * @param inheritable whether to expose the RequestAttributes as inheritable
     * for child threads (using an {@link InheritableThreadLocal})
     */
    public static void setRequestAttributes(RequestAttributes attributes, boolean inheritable) {
        if (attributes == null) {
            resetRequestAttributes();
        }
        else {
            if (inheritable) {
                inheritableRequestAttributesHolder.set(attributes);
                requestAttributesHolder.remove();
            }
            else {
                requestAttributesHolder.set(attributes);
                inheritableRequestAttributesHolder.remove();
            }
        }
    }

 (2) 呼叫鏈如下:

  可以看到是在一個filter 中存入的相關物件。

org.springframework.web.filter.RequestContextFilter 過濾器原始碼如下:

package org.springframework.web.filter;

import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

/**
 * Servlet Filter that exposes the request to the current thread,
 * through both {@link org.springframework.context.i18n.LocaleContextHolder} and
 * {@link RequestContextHolder}. To be registered as filter in {@code web.xml}.
 *
 * <p>Alternatively, Spring's {@link org.springframework.web.context.request.RequestContextListener}
 * and Spring's {@link org.springframework.web.servlet.DispatcherServlet} also expose
 * the same request context to the current thread.
 *
 * <p>This filter is mainly for use with third-party servlets, e.g. the JSF FacesServlet.
 * Within Spring's own web support, DispatcherServlet's processing is perfectly sufficient.
 *
 * @author Juergen Hoeller
 * @author Rod Johnson
 * @author Rossen Stoyanchev
 * @since 2.0
 * @see org.springframework.context.i18n.LocaleContextHolder
 * @see org.springframework.web.context.request.RequestContextHolder
 * @see org.springframework.web.context.request.RequestContextListener
 * @see org.springframework.web.servlet.DispatcherServlet
 */
public class RequestContextFilter extends OncePerRequestFilter {

    private boolean threadContextInheritable = false;


    /**
     * Set whether to expose the LocaleContext and RequestAttributes as inheritable
     * for child threads (using an {@link java.lang.InheritableThreadLocal}).
     * <p>Default is "false", to avoid side effects on spawned background threads.
     * Switch this to "true" to enable inheritance for custom child threads which
     * are spawned during request processing and only used for this request
     * (that is, ending after their initial task, without reuse of the thread).
     * <p><b>WARNING:</b> Do not use inheritance for child threads if you are
     * accessing a thread pool which is configured to potentially add new threads
     * on demand (e.g. a JDK {@link java.util.concurrent.ThreadPoolExecutor}),
     * since this will expose the inherited context to such a pooled thread.
     */
    public void setThreadContextInheritable(boolean threadContextInheritable) {
        this.threadContextInheritable = threadContextInheritable;
    }


    /**
     * Returns "false" so that the filter may set up the request context in each
     * asynchronously dispatched thread.
     */
    @Override
    protected boolean shouldNotFilterAsyncDispatch() {
        return false;
    }

    /**
     * Returns "false" so that the filter may set up the request context in an
     * error dispatch.
     */
    @Override
    protected boolean shouldNotFilterErrorDispatch() {
        return false;
    }

    @Override
    protected void doFilterInternal(
            HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {

        ServletRequestAttributes attributes = new ServletRequestAttributes(request, response);
        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);
        RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
        if (logger.isDebugEnabled()) {
            logger.debug("Bound request context to thread: " + request);
        }
    }

    private void resetContextHolders() {
        LocaleContextHolder.resetLocaleContext();
        RequestContextHolder.resetRequestAttributes();
    }

}

  可以看到相關的存、放操作都是在org.springframework.web.filter.RequestContextFilter#doFilterInternal 方法中完成的。

  到這裡也就明白了關於Request、Response 物件的存放原理,以及其自動注入基於JDK 動態代理的原理。