标签:spring ioc scope spring依赖注入 源码
抛出一个议点:BeanFactory是IOC容器,而ApplicationContex则是Spring容器。
什么是容器?Collection和Container这两个单词都有存放什么东西的意思,但是放在程序猿的世界,却注定是千差万别。Collection,集合,存放obj instanceof Class为true的一类对象,重点在于存放;Container,容器,可以存放各种各样的obj,但不仅仅是存放,他被称为容器,更重要的是他能管理存放对象的生命周期和依赖。
容器:用于存放对象,并能对存放对象进行生命周期管理和依赖管理。
Spring IOC容器是BeanFactory,也正是基于【容器】的论点。
在逻辑和源码分析之前,先做一些铺垫。对于使用Spring的程序猿来说,常用是ApplicationContext接口及其实现子类,ClasPathXmlApplicationContext、FileSystemXmlApplicationContext、XmlWebApplicationContext和AnnotationConfigWebApplicationContext,对于这四个类来说,他们都有一个共同的抽象父类AbstractRefreshableApplicationContext,而正是在该抽象父类中完成对BeanFactory的装饰。
public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext {
private Boolean allowBeanDefinitionOverriding;
private Boolean allowCircularReferences;
/** Bean factory for this context */
private DefaultListableBeanFactory beanFactory;
所有通过ApplicationContext接入使用Spring服务的,都是使用该bean工厂,而且最重要的是这个bean工厂实现了所有BeanFactory的接口、抽象类,拥有完整的Spring IOC逻辑。
第一节已经说过ApplicationContext常用的的四个子类都有一个公共的抽象父类AbstractRefreshableApplicationContext,在该类中对BeanFactory进行装饰,一个更重要的点是AbstractRefreshableApplicationContext的父类是AbstractApplicationContext,其refresh方法定义了整个Spring容器启动的过程。
也就是说,无论你采用哪一种ApplicationContext接入Spring容器,最终都会进入AbstractApplicationContext的refresh方法,完成Spring的启动。
源码分析是基于Spring IOC中提出的五点逻辑,发现一篇写的很全面的文章,因此决定不再写了,贴出来共享。
作为连接文章中未提及的web部分,本文予以补充,进行原理和源码的分析。
如上文继承关系所述内容,无论是哪一种ApplicationContext,最终都是通过AbstractBeanFactory.getBean(String)来获取Bean,节选部分代码。
// 从BeanDefinition中获取scope配置内容
String scopeName = mbd.getScope();
// this.scopes是一个Map<String, Scope>,用于存放Scope对象实例
// 这里如果是request则获取到RequestScope
// 如果是Session则获取到SessionScope
final Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'");
}
try {
// 这里是重点
// 通过Scope.get(String, ObjectFactory)接入到Spring IOC容器中
Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
}
});
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
} catch (IllegalStateException ex) {
throw new BeanCreationException(beanName,
"Scope '" + scopeName + "' is not active for the current thread; " +
"consider defining a scoped proxy for this bean if you intend to refer to it from a singleton", ex);
}
关于Scope的类结构如下图所示。
AbstractRequestAttributeScope定义了get(beanName, ObjectFactory)方法,通过该方法接入Spring IOC容器,源码如下所示。
public Object get(String name, ObjectFactory<?> objectFactory) {
// 这里很重要
// RequestContextHolder中通过ThreadLocal将RequestAttributes实例和当前线程绑定
// RequestAttributes在构造的时候需要传入HttpServletRequest,稍后会有源码分析和总结
RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
// 从当前线程绑定的RequestAttributes中获取对象(实际上是从HttpServletRequest的Map)
// 如果已经实例化过了,则不再实例化
// 否则进入Spring IOC过程获取对象
Object scopedObject = attributes.getAttribute(name, getScope());
if (scopedObject == null) {
scopedObject = objectFactory.getObject();
attributes.setAttribute(name, scopedObject, getScope());
}
return scopedObject;
}
现在存在的疑问是RequestAttributes、RequestContextHolder是个什么鬼?从哪里来?
要想使用Spring web scope,在web.xml中要配置一个侦听器RequestContextListener,所有谜题的答案都在这里。
package org.springframework.web.context.request;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.http.HttpServletRequest;
import org.springframework.context.i18n.LocaleContextHolder;
public class RequestContextListener implements ServletRequestListener {
private static final String REQUEST_ATTRIBUTES_ATTRIBUTE =
RequestContextListener.class.getName() + ".REQUEST_ATTRIBUTES";
// request创建的时候会触发该方法被调用执行
public void requestInitialized(ServletRequestEvent requestEvent) {
// 只支持HTTP方式
if (!(requestEvent.getServletRequest() instanceof HttpServletRequest)) {
throw new IllegalArgumentException(
"Request is not an HttpServletRequest: " + requestEvent.getServletRequest());
}
// 获取当前request
HttpServletRequest request = (HttpServletRequest) requestEvent.getServletRequest();
// 创建ServletRequestAttributes对象
// 该对象只是简单的封装了request,实际上scope为request或session的Bean都存储在request或session中
ServletRequestAttributes attributes = new ServletRequestAttributes(request);
// 和request水乳交融
request.setAttribute(REQUEST_ATTRIBUTES_ATTRIBUTE, attributes);
LocaleContextHolder.setLocale(request.getLocale());
// RequestContextHolder中封装了一个ThreadLocal
// 因此这句代码的含义是将ServletRequestAttributes与当前线程绑定
// 因为是一个静态方法,因此这意味着无论在哪里
// 只要调用RequestContextHolder.getRequestAttributes().getAttribute().setAttribute()都能将Bean和request进行绑定
// 而且在一次request的生命周期中不会重复创建对象
RequestContextHolder.setRequestAttributes(attributes);
}
// request被销毁的时候会触发该方法调用执行
public void requestDestroyed(ServletRequestEvent requestEvent) {
ServletRequestAttributes attributes = null;
Object reqAttr = requestEvent.getServletRequest().getAttribute(REQUEST_ATTRIBUTES_ATTRIBUTE);
if (reqAttr instanceof ServletRequestAttributes) {
attributes = (ServletRequestAttributes) reqAttr;
}
RequestAttributes threadAttributes = RequestContextHolder.getRequestAttributes();
if (threadAttributes != null) {
// We're assumably within the original request thread...
LocaleContextHolder.resetLocaleContext();
RequestContextHolder.resetRequestAttributes();
if (attributes == null && threadAttributes instanceof ServletRequestAttributes) {
attributes = (ServletRequestAttributes) threadAttributes;
}
}
if (attributes != null) {
attributes.requestCompleted();
}
}
}
这句代码RequestContextHolder.setRequestAttributes(attributes)是将Spring web scope与Spring IOC结合的关键。
public abstract class RequestContextHolder {
// 将ServletRequestAttributes与当前线程绑定
private static final ThreadLocal<RequestAttributes> requestAttributesHolder =
new NamedThreadLocal<RequestAttributes>("Request attributes");
private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder =
new NamedInheritableThreadLocal<RequestAttributes>("Request context");
// 1 第一步
public static void setRequestAttributes(RequestAttributes attributes) {
setRequestAttributes(attributes, false);
}
// 2 第二步,inheritable=false
public static void setRequestAttributes(RequestAttributes attributes, boolean inheritable) {
if (attributes == null) {
resetRequestAttributes();
} else {
if (inheritable) {
inheritableRequestAttributesHolder.set(attributes);
requestAttributesHolder.remove();
}
else {
// 3 第三步,将ServletRequestAttributes与当前线程绑定
requestAttributesHolder.set(attributes);
inheritableRequestAttributesHolder.remove();
}
}
}
//其他源码,略
}
至此,整个过程结束。session的过程几乎和request一致,只是生命周期不一样,多了一把锁。总结一下上述源码分析的内容。
总结:Spring Bean所有scope定义为request、session、globalsession的Bean都不会主动初始化,当第一次通过Spring IOC来getBean的时候才会实例化并进行依赖注入,但是在实例化的时候需要借助于RequestContextHolder(ThreadLocal模式)来将Bean与当前线程中的HttpServletRequest来进行绑定,并在scope的生命周期中缓存在相应的域对象中(request或session)。
最后补充一点,ServletRequestAttributes的getAttribute和setAttribute。在getBean的时候,会先从ServletRequestAttributes.getAttribute中获取Bean,如果获取到则返回,否则进行IOC实例化,并调用ServletRequestAttributes.setArrtibute进行缓存,实际上在setAttribute方法中是调用request.setAttribute来完成。
public class ServletRequestAttributes extends AbstractRequestAttributes {
public static final String DESTRUCTION_CALLBACK_NAME_PREFIX =
ServletRequestAttributes.class.getName() + ".DESTRUCTION_CALLBACK.";
private final HttpServletRequest request;
private HttpServletResponse response;
private volatile HttpSession session;
private final Map<String, Object> sessionAttributesToUpdate = new ConcurrentHashMap<String, Object>(1);
// 1 ServletRequestAttributes对象的创建,必须要一个HttpServletRequest实例
public ServletRequestAttributes(HttpServletRequest request) {
Assert.notNull(request, "Request must not be null");
this.request = request;
}
public ServletRequestAttributes(HttpServletRequest request, HttpServletResponse response) {
this(request);
this.response = response;
}
// 转调request.getAttribute或session.getAttribute
public Object getAttribute(String name, int scope) {
if (scope == SCOPE_REQUEST) {
if (!isRequestActive()) {
throw new IllegalStateException(
"Cannot ask for request attribute - request is not active anymore!");
}
return this.request.getAttribute(name);
}
else {
HttpSession session = getSession(false);
if (session != null) {
try {
Object value = session.getAttribute(name);
if (value != null) {
this.sessionAttributesToUpdate.put(name, value);
}
return value;
}
catch (IllegalStateException ex) {
// Session invalidated - shouldn't usually happen.
}
}
return null;
}
}
// 转调reqeust.setAttribute或session。setAttribute
public void setAttribute(String name, Object value, int scope) {
if (scope == SCOPE_REQUEST) {
if (!isRequestActive()) {
throw new IllegalStateException(
"Cannot set request attribute - request is not active anymore!");
}
this.request.setAttribute(name, value);
}
else {
HttpSession session = getSession(true);
this.sessionAttributesToUpdate.remove(name);
session.setAttribute(name, value);
}
}
至此,分析结束。
附注:
一开始想把Spring整个IOC逻辑理出来,但是发现已经有写的比较好的文章,因此就决定不写了。
但是阅读之后发现web scope中的IOC过程并没有分析出来,因此文章后半部分针对scope=reqeust、scope=session进行了实现源码与原理的分析过程。
最后,本文如有错漏,烦请不吝指正,谢谢!
版权声明:本文为博主原创文章,未经博主允许不得转载。
【Spring】Spring IOC原理及源码解析之scope=request、session
标签:spring ioc scope spring依赖注入 源码
原文地址:http://blog.csdn.net/reliveit/article/details/47291057