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(); } } }
以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 动态代理的原理。