Spring容器中的bean可以分为5个范围
-
prototype:为每一个bean请求提供一个实例。
-
singleton:默认,每个容器中只有一个bean的实例,单例的模式由BeanFactory自身来维护。
-
request:为每一个网络请求创建一个实例,在请求完成以后,bean会失效并被垃圾回收器回收。
-
session:与request范围类似,确保每个session中有一个bean的实例,在session过期后,bean会随之失效。
-
global-session:全局作用域,global-session和Portlet应用相关。当你的应用部署在Portlet容器中工作时,它包含很多portlet。如果你想要声明让所有的portlet共用全局的存储变量的话,那么这全局变量需要存储在global-session中。全局作用域与Servlet中的session作用域效果相同。
<!-- 默认的作用域:singleton -->
<bean id="p1" class="com.abc.Person" />
<!-- 指定的作用域:prototype -->
<bean id="p2" class="com.abc.Person" scope="prototype" />
下面是一个测试类:
public class BeanTest {
public static void main(String args[]) {
//加载类路径下的beans.xml文件以初始化Spring容器
ApplicationContext context = new ClassPathXmlApplicationContext();
//分两次分别取同一个Bean,比较二者是否是同一个对象
System.out.println(context.getBean("p1") == context.getBean("p1"));
System.out.println(context.getBean("p2") == context.getBean("p2"));
}
}
执行结果分别是:true和false
从结果可以看出,正如上文所述:对于singleton作用域的Bean,每次请求该id的Bean,都将返回同一个实例,而prototype作用域的Bean, 每次请求都将产生全新的实例。
自定义作用域
如果BeanDefinition
既不是singleton
,也不是prototype
,那么就会执行自定义作用域的创建规则:
- 获取
BeanDefinition
的scope
属性值。 - 从
BeanFactory
的作用域缓存scopes
中获取对应的作用域。 - 调用
scope.get()
方法,执行自定义创建规则。 - 后处理:Spring AOP功能入口,与
singleton
和prototype
相同。
// 1、获取BeanDefinition的scope属性值
String scopeName = mbd.getScope();
// 2、从BeanFactory的作用域缓存scopes中获取对应的作用域
Scope scope = this.scopes.get(scopeName);
try {
// 3、执行自定义创建规则
Object scopedInstance = scope.get(beanName, () -> {
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
});
// 4、后处理
beanInstance = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
catch (IllegalStateException ex) {
throw new ScopeNotActiveException(beanName, scopeName, ex);
}
从以上源码中我们可以得出自定义作用域的三个步骤:
- 创建作用域实现类:实现
org.springframework.beans.factory.config.Scope
接口,实现其get()
方法。
mermaid classDiagram class Scope Scope : +get(String name, ObjectFactory<?> objectFactory)
- 将作用域实现类注册到
BeanFactory
的scopes
缓存中:key为作用域名,value为自定义作用域对象(org.springframework.beans.factory.config.ConfigurableBeanFactory#registerScope
)。 - 配置
Bean
时,指定对应的作用域名。
内置作用域和自定义作用域的区别?
内置作用域的作用域名和bean
创建规则已经写死到Spring IoC容器中。
自定义作用域通过自定义的作用域名从BeanFactory
的scopes
缓存中找到自定义作用域实现类,根据其中实现的get()
方法创建bean
。同时,自定义作用域的bean
对象存放于自定义的缓存中。
知识来源:马士兵教育