在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中配置为:
<bean id="hello" class="com.zjp.bean.BeanHello" scope="singleton" init-method="init" destroy-method="xmlDestroy"/>
或者默认不写scope。同时也支持注解的方式:
@Service
@Scope("singleton")
public class BeanHello {}
2、prototype(每次请求都会创建一个新的bean实例)
当设置bean的作用域为prototype时,说明一个bean会对应多个实例对象,也就是在getBean或者注入的方式都会创建一个新的bean实例。使用prototype作用域时,容器中并没有对这个bean进行实例化,而是每次调用的时候再去创建,那什么时候使用prototype呢?对有状态的 bean 应该使用 prototype 作用域,而对无状态的 bean 则应该使用 singleton 作用域。配置方式也是两种:
<bean id="hello" class="com.zjp.bean.BeanHello" scope="prototype" init-method="init" destroy-method="xmlDestroy"/>
注解:
@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<Object>() {
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<Object>() {
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的作用域就实现了。