spring bean的作用域

在spring中,那些组成用用程序的主体以及由springIOC容器所管理的对象称之为bean。也就是说bean是由IOC容器初始化、装配以及管理的对象,除此之外,bean就跟普通的对象一样。然而bean的定义以及bean之间的相互依赖关系是通过配置元数据来描述。
Spring中的bean默认都是单例的,这些单例的Bean在多线程中是怎么保证线程安全的呢?
例如对于Web应用来说,Web容器对于每个用户请求都创建一个单独的Sevlet线程处理请求,引入spring之后,每个Action都是单例的,那么对于spring托管的单例Service Bean,如何保证其安全呢?Spring的单例是基于BeanFactory也就是Spring容器的,单例Bean在这个容器中只有一个,Java的单例是基于JVM的,每个JVM内只有一个实例。Spring中除了单例模式还有其他的作用域。
一、Bean的作用域
创建一个bean的定义,其实就是什么时候创建一个ban,在spring框架中支持五种作用域,分别如下:

  • Singleton 在Spring IOC 容器仅存在一个Bean实例,Bean以单例方式存在,这个是默认值。
  • prototype 每次从容器调用bean时,都会返回一个新的实例,也就是每次调用getBean()时都会实例化一个新的bean。
  • request 每次HTTP请求都会创建一个新的Bean,该作用于仅适用于web环境
  • session 每个HTTP Session共享一个Bean,不同的Session使用不同的Bean,同样只适用于web环境。
  • Global Session 一般作用于Portlet应用环境,只作用于Web环境。
    五种作用域中,其中request、session、global session三种作用域仅适用于web环境。
    1、signleton(单例模式)
    当一个bean的作用域为signleton,那么在spring IOC容器中只会存在一个共享的bean实例,对于所有的bean的请求,只要id与定义的bean相匹配,那么就会返回这个实例。Spring中设置singleton或者缺省的条件下,在容器建立的过程中就会创建bean对象,每次getBean都是同一个对象。在spring中配置为:

或者默认不写scope。同时也支持注解的方式:

@Service
@Scope("singleton")
public class BeanHello {}

2、prototype(每次请求都会创建一个新的bean实例)
当设置bean的作用域为prototype时,说明一个bean会对应多个实例对象,也就是在getBean或者注入的方式都会创建一个新的bean实例。使用prototype作用域时,容器中并没有对这个bean进行实例化,而是每次调用的时候再去创建,那什么时候使用prototype呢?对有状态的 bean 应该使用 prototype 作用域,而对无状态的 bean 则应该使用 singleton 作用域。配置方式也是两种:


注解:

@Service
@Scope("prototype")
public class BeanHello {}

后面的三种作用域就不一一讲解了,通常都是通过xml或者注解的方式进行配置。那么这几种作用域在spring源码中是怎么实现的呢?
二、源码
Spring中有一个很重要的类BeanDefinition,这个包含了bean的信息,在bean初始化的过程将信息封装到这个类中。

public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
	String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
	String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;
	boolean isSingleton();
	boolean isPrototype();
String getScope();
}

从中可以得出,在初始化bean实例的时候就会根据BeanDefinition来判断是singleton还是prototype。但是如果既不是singleton也是prototype那怎么处理呢?在spring中有具体的逻辑来处理其他情况,先看一个Scope类。

public interface Scope { 
//通过一个名称获取Bean实例 
Object get(String name, ObjectFactory objectFactory); 
//销毁指定名称的Bean 
Object remove(String name); 
//为指定名称的Bean注册销毁时的回调函数 
void registerDestructionCallback(String name, Runnable callback); 
Object resolveContextualObject(String key); 
String getConversationId(); 
}

在spring容器启动的时候会对多个scope进行注册,从源码中也可以看出来request、session、global session是web项目特有的作用域。在容器中分别对这些作用域进行注册。

public static void registerWebApplicationScopes(ConfigurableListableBeanFactory beanFactory, ServletContext sc) {
		// 注册几个web项目特有的作用域
		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(HttpSession.class, new SessionObjectFactory());
		beanFactory.registerResolvableDependency(WebRequest.class, new WebRequestObjectFactory());
		if (jsfPresent) {
			FacesDependencyRegistrar.registerFacesDependencies(beanFactory);
		}
	}

RequestScope、SessionScope都是Scope接口的子类,同时也是AbstractRequestAttributesScope的子类,AbstractRequestAttributesScope实现了获取bean的方法,也就是说会根据不同的作用域初始化bean。
设置了作用域时候在初始化的时候如何根据不同的作用域获取bean呢?

// Create bean instance. 单例模式
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, new ObjectFactory() {
	public Object getObject() throws BeansException {
		try {
			return createBean(beanName, mbd, args);
		}
		catch (BeansException ex) {
			// Explicitly remove instance from singleton cache: It might have been put there
			// eagerly by the creation process, to allow for circular reference resolution.
			// Also remove any beans that received a temporary reference to the bean.
			destroySingleton(beanName);
			throw ex;
		}
	}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
// 每次请求都创建
else if (mbd.isPrototype()) {
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
	beforePrototypeCreation(beanName);
	prototypeInstance = createBean(beanName, mbd, args);
}
finally {
	afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
// 如果两种方式都不是
else {
String scopeName = mbd.getScope();
// 同时在上下文中注册的类型进行初始化
final Scope scope = this.scopes.get(scopeName);
if (scope == null) {
	throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'");
}
try {
	// 通过scope初始化bean
	Object scopedInstance = scope.get(beanName, new ObjectFactory() {
		public Object getObject() throws BeansException {
			beforePrototypeCreation(beanName);
			try {
				return createBean(beanName, mbd, args);
			}
			finally {
				afterPrototypeCreation(beanName);
			}
		}
	});
	bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
 
  

那么除了singleton和prototype初始化bean的逻辑就在Scope的子类AbstractRequestAttributesScope 中实现了。

public abstract class AbstractRequestAttributesScope implements Scope {

	/**
	 * 初始化bean
	 */
	public Object get(String name, ObjectFactory objectFactory) {
		RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
		Object scopedObject = attributes.getAttribute(name, getScope());
		if (scopedObject == null) {
			// 如果这个对象不存在,那么创建一个同时保存到attributes
			scopedObject = objectFactory.getObject();
			attributes.setAttribute(name, scopedObject, getScope());
		}
		return scopedObject;
	}

	public Object remove(String name) {
		RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
		Object scopedObject = attributes.getAttribute(name, getScope());
		if (scopedObject != null) {
			attributes.removeAttribute(name, getScope());
			return scopedObject;
		}
		else {
			return null;
		}
	}

	public void registerDestructionCallback(String name, Runnable callback) {
		RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
		attributes.registerDestructionCallback(name, callback, getScope());
	}

	public Object resolveContextualObject(String key) {
		RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
		return attributes.resolveReference(key);
	}


	/**
	 * Template method that determines the actual target scope.
	 * @return the target scope, in the form of an appropriate
	 * {@link RequestAttributes} constant
	 * @see RequestAttributes#SCOPE_REQUEST
	 * @see RequestAttributes#SCOPE_SESSION
	 * @see RequestAttributes#SCOPE_GLOBAL_SESSION
	 */
	protected abstract int getScope();

}

主要是从RequestContextHolder获取attributes,attributes中包含了对应的bean,那么RequestContextHolder中的bean又是怎么获取到的?

/**
	 * 获取当前线程绑定的RequestAttributes
	 */
	public static RequestAttributes getRequestAttributes() {
		RequestAttributes attributes = requestAttributesHolder.get();
		if (attributes == null) {
			attributes = inheritableRequestAttributesHolder.get();
		}
		return attributes;
	}

	/**
	 * 获取当前线程绑定的RequestAttributes
	 */
	public static RequestAttributes currentRequestAttributes() throws IllegalStateException {
		RequestAttributes attributes = getRequestAttributes();
		if (attributes == null) {
			if (jsfPresent) {
				attributes = FacesRequestAttributesFactory.getFacesRequestAttributes();
			}
			if (attributes == null) {
				throw new IllegalStateException("");
			}
		}
		return attributes;
	}

获取当前线程绑定的RequestAttributes ,如果RequestAttributes 为null,说明没有HttpRequest请求线程存在,也就是说当前容器还在初始化阶段,在容器初始化阶段就尝试实例化一个与HttpRequest绑定的Bean会报错,也就说scope为request或session的Bean不能在容器初始化阶段实例化,不能直接注入scope为singleton的Bean中。RequestContextHolder 有两个ThreadLocal类型的静态成员,专门用于存储Http请求的信息。

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();
		// 创建request
		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, startTime, failureCause);
		}
	}
protected ServletRequestAttributes buildRequestAttributes(
			HttpServletRequest request, HttpServletResponse response, RequestAttributes previousAttributes) {

		if (previousAttributes == null || previousAttributes instanceof ServletRequestAttributes) {
			return new ServletRequestAttributes(request);
		}
		else {
			return null;  // preserve the pre-bound RequestAttributes instance
		}
	}
	private void initContextHolders(
			HttpServletRequest request, LocaleContext localeContext, RequestAttributes requestAttributes) {

		if (localeContext != null) {
			LocaleContextHolder.setLocaleContext(localeContext, this.threadContextInheritable);
		}
		//将当前Http请求的Request,Response的相关信息设置到ThreadLocal对象中。
		if (requestAttributes != null) {
			RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
		}
		if (logger.isTraceEnabled()) {
			logger.trace("Bound request context to thread: " + request);
		}
	}
	// 请求结束,清空threadLocal信息
	private void resetContextHolders(HttpServletRequest request,
			LocaleContext prevLocaleContext, RequestAttributes previousAttributes) {

		LocaleContextHolder.setLocaleContext(prevLocaleContext, this.threadContextInheritable);
		RequestContextHolder.setRequestAttributes(previousAttributes, this.threadContextInheritable);
		if (logger.isTraceEnabled()) {
			logger.trace("Cleared thread-bound request context: " + request);
		}
	}

这样spring的作用域就实现了。

你可能感兴趣的:(spring,源码)