使用Spring框架定义一个bean时,可以使用@Scope注解定义bean的作用域,最常见的就是singleton和prototype两种类型,同时,在WEB工程中还衍生出了@RequestScope、@SessionScope、@ApplicationScope三种范围的作用域注解
使用java注解定义一个bean
@Configuration
public class Config {
@Bean
@Scope // 默认单例模式
public User user() {
return new User();
}
}
当Spring容器启动时,会根据配置的扫描路径扫描所有的类信息,搜集所有标注@Bean注解的方法构造BeanDefinition,每个BeanDefinition都保存了相应待创建的bean模板信息(类似java通过Class信息反射创建一个对象,Spring通过BeanDefinition创建一个bean)
BeanDefinition接口抽象实现类AbstractBeanDefinition有如下相关方法:
public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor
implements BeanDefinition, Cloneable {
@Nullable
private String scope = SCOPE_DEFAULT;
@Override
public void setScope(@Nullable String scope) { this.scope = scope; }
@Override
@Nullable
public String getScope() { return this.scope; }
@Override
public boolean isSingleton() {
return SCOPE_SINGLETON.equals(this.scope) || SCOPE_DEFAULT.equals(this.scope);
}
@Override
public boolean isPrototype() {
return SCOPE_PROTOTYPE.equals(this.scope);
}
}
通过上述源码可以得知BeanDefinition记录了一个bean的Scope信息,一般来说,原型和单例梁两种作用域是互斥的,同时,如果使用了自定义的Scope则既不是singleton也不是prototype
那么,作用域Scope在创建bean的过程中起到什么作用?
在AbstractBeanFactory中,有如下获取bean的方法,若从作用域中获取不到bean,则通过AbstractBeanFactory#createBean创建目标bean
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport
implements ConfigurableBeanFactory {
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
// ...
try {
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
// Guarantee initialization of beans that the current bean depends on.
// 若当前bean依赖其他bean,先创建被依赖的bean
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
if (isDependent(beanName, dep)) {
// 省略异常...
}
registerDependentBean(dep, beanName);
try {
getBean(dep);
} catch (NoSuchBeanDefinitionException ex) {
// 省略异常...
}
}
}
// 从以下代码可以获知,单例、原型、以及自定义的scope
// 在创建一个bean的时候选择不同的分支,逻辑互斥
// 可以简单的理解scope表示一个bean的“圈子”
// 获取一个“圈子”内的bean前先根据圈子的定义在“圈子”内查询,若查询不到则由容器创建
// Create bean instance.
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
} catch (BeansException ex) {
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) {
// 省略异常...
}
try {
// 从自定义的scope中获取bean
Object scopedInstance = scope.get(beanName, () -> {
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
} finally {
afterPrototypeCreation(beanName);
}
});
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
} catch (IllegalStateException ex) {
// 省略异常...
}
}
} catch (BeansException ex) {
cleanupAfterBeanCreationFailure(beanName);
throw ex;
}
}
// ...
}
从上面源码可以得出结论,创建bean的时候,会根据定义信息BeanDefinition判断scope类型,如果是自定义的scope,先尝试从scope获取bean对象,若获取不到则由容器创建
可以从Spring提供的一个自定义Scope范例一探究竟,如下:
// 注册Scope方式:
// 1. org.springframework.beans.factory.config.ConfigurableBeanFactory#registerScope
// 2. 定位org.springframework.beans.factory.config.CustomScopeConfigurer类,将Scope注入属性中
public class SimpleThreadScope implements Scope {
private static final Log logger = LogFactory.getLog(SimpleThreadScope.class);
// 线程安全的scope
private final ThreadLocal<Map<String, Object>> threadScope =
new NamedThreadLocal<Map<String, Object>>("SimpleThreadScope") {
@Override
protected Map<String, Object> initialValue() {
return new HashMap<>();
}
};
@Override
public Object get(String name, ObjectFactory<?> objectFactory) {
Map<String, Object> scope = this.threadScope.get();
Object scopedObject = scope.get(name);
// 如果当前的Scope获取不到bean
if (scopedObject == null) {
// 根据AbstractBeanFactory中的源码,交给createBean方法创建bean对象
scopedObject = objectFactory.getObject();
scope.put(name, scopedObject);
}
return scopedObject;
}
@Override
@Nullable
public Object remove(String name) {
Map<String, Object> scope = this.threadScope.get();
return scope.remove(name);
}
@Override
public void registerDestructionCallback(String name, Runnable callback) {
logger.warn("SimpleThreadScope does not support destruction callbacks. " +
"Consider using RequestScope in a web environment.");
}
@Override
@Nullable
public Object resolveContextualObject(String key) {
return null;
}
@Override
public String getConversationId() {
return Thread.currentThread().getName();
}
}