Spring中的Scope概念和原理解析
Scope的基本概念
Scope定义了 Bean 在容器中的生命周期和可见范围。Scope决定了:
- 容器何时创建该 Bean?
- 创建多少个实例?
- 这些实例在什么上下文中共享?
- 何时销毁?
Spring内置的标准Scope
Spring提供了6种标准Scope
1. singleton
默认的Scope。在Spring Ioc容器中,每个Bean定义只对应一个实例。平时使用的@Service、@Controller、@Component创建的Bean都是这个Scope。
2. prototype
一个Bean的定义可以有多个实例。每次从容器中获取Bean时都创建一个新实例。
使用方式:
@Component
@org.springframework.context.annotation.Scope("prototype")
public class RefreshScope implements Scope {
}
Spring 容器不会管理prototype实例的销毁工作。Bean的销毁和资源释放需要由客户端代码来完成。
3. request
仅在Web应用中有效;每个Http请求创建一个实例。
使用方式:
@RequestScope
@Component
public class LoginAction {
// ...
}
4. session
每个HttpSession对应一个实例
使用方式:
@SessionScope
@Component
public class UserPreferences {
// ...
}
5. application
一个web application对应一个实例。使用方式:
@ApplicationScope
@Component
public class AppPreferences {
// ...
}
6. websocket
对应WebSocket Session范围。
使用方式:
@Component
@Scope(scopeName = "websocket", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class MyBean {
@PostConstruct
public void init() {
// Invoked after dependencies injected
}
// ...
@PreDestroy
public void destroy() {
// Invoked when the WebSocket session ends
}
}
实现原理
1. Scope接口
public interface Scope {
/**
* 获得bean,通过调用ObjectFactory#getObject()来创建bean
*/
Object get(String name, ObjectFactory<?> objectFactory);
/**
* 移除一个bean
*/
@Nullable
Object remove(String name);
/**
* 注册一个bean的销毁函数
*/
void registerDestructionCallback(String name, Runnable callback);
/**
* 解析给定键的上下文对象
*/
@Nullable
Object resolveContextualObject(String key);
/**
* 会话ID
*/
@Nullable
String getConversationId();
}
子类通过实现接口中的方法来实现不同的功能。例如RequestScope中的get方法为:
@Override
public Object get(String name, ObjectFactory<?> objectFactory) {
RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
Object scopedObject = attributes.getAttribute(name, getScope());
if (scopedObject == null) {
scopedObject = objectFactory.getObject();
attributes.setAttribute(name, scopedObject, getScope());
// Retrieve object again, registering it for implicit session attribute updates.
// As a bonus, we also allow for potential decoration at the getAttribute level.
Object retrievedObject = attributes.getAttribute(name, getScope());
if (retrievedObject != null) {
// Only proceed with retrieved object if still present (the expected case).
// If it disappeared concurrently, we return our locally created instance.
scopedObject = retrievedObject;
}
}
return scopedObject;
}
RequestScope将Bean存储在一个线程变量中,以此来实现Bean在一个Http Request中传播,需要配合RequestContextListener或者RequestContextFilter或者DispatcherServlet来删除线程变量中保存的Bean。
2. Scope中的方法何时被调用?
Scope#get方法在调用BeanFactory#getBean方法时被调用,逻辑如下:
protected <T> T doGetBean() {
String beanName = transformedBeanName(name);
Object beanInstance;
//尝试从缓存中获取单例,如果bean是非单例模式,返回的是null
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
//代码省略,非singlton模式的bean不会走到这个分支
}
else {
//如果出现了循环依赖则报错
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
//尝试从父BeanFactory中获取该Bean
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// Not found -> check parent.
//代码省略
}
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
//代码省略
//开始创建Bean
//单例模式
if (mbd.isSingleton()) {
//代码简化
sharedInstance = getSingleton();
}
//原型模式
else if (mbd.isPrototype()) {
//代码简化
createBean();
}
//使用Scope获取Bean
else {
String scopeName = mbd.getScope();
if (!StringUtils.hasLength(scopeName)) {
throw new IllegalStateException();
}
Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException();
}
//调用scope#get获取Bean,第二个参数是一个bean创建的lambda表达式
Object scopedInstance = scope.get(beanName, () -> {
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
});
beanInstance = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
}
return beanInstance;
}
3. @Scope注解
与@Component搭配使用,标记一个类的scope名称以及proxyMode 代理模式。
proxyMode是一个枚举值,有4种取值:
/**
* 默认类型,通常等同于NO
*/
DEFAULT,
/**
* 不创建代理
*/
NO,
/**
* 创建JDK动态代理,基于接口
*/
INTERFACES,
/**
* 基于类创建代理,使用CGLIB
*/
TARGET_CLASS
这个注解在何时被读取呢?
在扫描bean时被读取,具体方法为ClassPathBeanDefinitionScanner#doScan。

bean在扫描时,如果指定了Scope并且,proxyMode为INTERFACES或TARGET_CLASS时会创建代理类。创建的代理类为ScopedProxyFactoryBean,主要的代理逻辑是:每次方法执行前都从BeanFactory中获取bean,然后将方法绑定到真正的Bean上执行。
4. scope bean的代理
生成scope代理类的目的是为了使用的方便。比如在一个singleton的类中使用一个scope为request的类,在每次使用时你都需要从scope中获取该Bean,有了代理后将简化该过程。例子:
@Component
@RequestScope
public class MyService {
public String getUser() {
return "";
}
}
@Component
public class AnotherService {
@Autowired
private MyService myservice;//这里注入的是一个代理类,每次方法调用前从beanFactory中获取bean,并将方法绑定到真正的bean上,不用每次都从BeanFactory中获取
}
ScopedProxyFactoryBean源码分析:
ScopedProxyFactoryBean是一个FacotryBean,getObject方法实现为:
@Override
public Object getObject() {
if (this.proxy == null) {
throw new FactoryBeanNotInitializedException();
}
return this.proxy;
}
返回的是一个proxy。
proxy的生成:
@Override
public void setBeanFactory(BeanFactory beanFactory) {
if (!(beanFactory instanceof ConfigurableBeanFactory)) {
throw new IllegalStateException("Not running in a ConfigurableBeanFactory: " + beanFactory);
}
ConfigurableBeanFactory cbf = (ConfigurableBeanFactory) beanFactory;
this.scopedTargetSource.setBeanFactory(beanFactory);
ProxyFactory pf = new ProxyFactory();
pf.copyFrom(this);
//最关键的是这一行
pf.setTargetSource(this.scopedTargetSource);
Assert.notNull(this.targetBeanName, "Property 'targetBeanName' is required");
Class<?> beanType = beanFactory.getType(this.targetBeanName);
if (beanType == null) {
throw new IllegalStateException("Cannot create scoped proxy for bean '" + this.targetBeanName +
"': Target type could not be determined at the time of proxy creation.");
}
if (!isProxyTargetClass() || beanType.isInterface() || Modifier.isPrivate(beanType.getModifiers())) {
pf.setInterfaces(ClassUtils.getAllInterfacesForClass(beanType, cbf.getBeanClassLoader()));
}
// Add an introduction that implements only the methods on ScopedObject.
ScopedObject scopedObject = new DefaultScopedObject(cbf, this.scopedTargetSource.getTargetBeanName());
pf.addAdvice(new DelegatingIntroductionInterceptor(scopedObject));
// Add the AopInfrastructureBean marker to indicate that the scoped proxy
// itself is not subject to auto-proxying! Only its target bean is.
pf.addInterface(AopInfrastructureBean.class);
this.proxy = pf.getProxy(cbf.getBeanClassLoader());
}
最关键的是这行代码:
pf.setTargetSource(this.scopedTargetSource);
targetSource的类为:
public class SimpleBeanTargetSource extends AbstractBeanFactoryBasedTargetSource {
@Override
public Object getTarget() throws Exception {
return getBeanFactory().getBean(getTargetBeanName());
}
}
getTarget方法在每次进入代理方法时被调用,也即每次都从beanFacotory中获取bean,这个bean实际从scope子类中获取,之后方法的执行会绑定到新获取的bean上执行。
应用
Spring Cloud中的RefreshScope就是基于Scope实现的,每次配置更新后,清除缓存中的Bean,然后重新创建、组装bean,之后就能获取最新的配置项。

浙公网安备 33010602011771号