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 implementsObjectFactory<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 動態代理的原理。