1. 什么是Scope属性
在上一章使用Bean标签中,已经尝试配置过Scope属性,比如将其设置成singleton表示该bean对象是个单例对象,在本章中将继续深入了解该属性。
2. 带着疑问阅读
- Scope具体由什么作用?
- 常见的Scope属性有那些?
- 如何自定义Scope值?
3. 基础使用
先创建bean分别以singleton和prototype来标识这两个java类。
public class SingletonModel{
}
public class PrototypeModel{
}
创建一个xml,将SigletonModel和PrototypeModel注入到spring的容器中。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 单例的bean -->
<bean id="singletonModel" class="com.qiupeng.bean.ioc.model.SingletonModel"/>
<!-- 多例的bean -->
<bean id="prototypeModel" class="com.qiupeng.bean.ioc.model.PrototypeModel" scope="prototype"/>
</beans>
创建一个测试类,对上述代码进行测试
public class ScopeTest {
private static final Logger logger = LogManager.getLogger(BeanDiTest.class);
GenericApplicationContext context;
@BeforeEach
public void testBefore() {
context = new GenericApplicationContext();
new XmlBeanDefinitionReader(context).loadBeanDefinitions("classpath:/bean/application-scope.xml");
context.refresh();
}
@Test
public void test(){
logger.info("111");
}
}
测试代码已经编写完成后了,接下来我们来验证Bean的创建时机和作用范围。
-
SigletonModel
通过调试程序可以看见SigletonModel对象是在创建完成容器后就已经存在了,并且在后续调用getBean函数都是返回一个对象,说明了它的创建时机在容器创建完成后就已经创建完成了,作用域在全局都是生效的 -
PrototypeModel
在SigletonModel例子中已经确定了PrototypeModel在容器创建完成后是不存在该对象实例的,而是需要调用getBean来获取和创建bean实例,通过上面输出结果可以发现每次调用getBean都会返回一个新的bean实例,说明PrototypeModel的作用域就在获取bean开始到不使用这个bean实例就结束了。
3. 自定义Scope属性
定义一个bean的作用域在同一个线程中获取到的bean对象是同一个。
自定义一个scope
public class ThreadScope implements Scope {
/**
* 创建一个基于线程的存储空间
*/
private final ThreadLocal<Map<String, Object>> local = ThreadLocal.withInitial(HashMap::new);
@Override
public Object get(String name, ObjectFactory<?> objectFactory) {
Object o = local.get().get(name);
if (o == null) {
o = objectFactory.getObject();
local.get().put(name, o);
}
return o;
}
@Override
public Object remove(String name) {
return local.get().remove(name);
}
@Override
public void registerDestructionCallback(String name, Runnable callback) {
}
@Override
public Object resolveContextualObject(String key) {
return null;
}
@Override
public String getConversationId() {
return String.valueOf(Thread.currentThread().getId());
}
定义一个scope为threadScope的java类
public class ThreadModel {
}
修改xml将ThreadModel 添加到容器管理,并且将自定义的scope设置到CustomScopeConfigurer中。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 自定义scope属性 -->
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="threadScope">
<bean class="com.qiupeng.bean.ioc.ThreadScope"/>
</entry>
</map>
</property>
</bean>
<bean id="threadModel" class="com.qiupeng.bean.ioc.model.ThreadModel" scope="threadScope" />
</beans>
编写测试类
public class ScopeTest {
private static final Logger logger = LogManager.getLogger(BeanDiTest.class);
GenericApplicationContext context;
@BeforeEach
public void testBefore() {
context = new GenericApplicationContext();
new XmlBeanDefinitionReader(context).loadBeanDefinitions("classpath:/bean/application-scope.xml");
//ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
//beanFactory.registerScope("threadScope", new ThreadScope());
context. Refresh();
}
@Test
public void testThreadScope() throws InterruptedException {
for (int i = 0; i < 5; i++) {
Runnable runnable = () -> {
for (int j = 0; j < 5; j++) {
ThreadModel threadModel = context.getBean(ThreadModel.class);
logger.info("线程名称:" + Thread.currentThread().getName() + ",testScopeModel=" + threadModel);
}
};
new Thread(runnable).start();
}
Thread.sleep(5000);
}
}
运行结果
[INFO ] 2023-07-04 16:32:23,194 com.qiupeng.bean.di.BeanDiTest - 线程名称:Thread-2,testScopeModel=com.qiupeng.bean.ioc.model.ThreadModel@7882f6f8
[INFO ] 2023-07-04 16:32:23,194 com.qiupeng.bean.di.BeanDiTest - 线程名称:Thread-1,testScopeModel=com.qiupeng.bean.ioc.model.ThreadModel@3b6fcb57
[INFO ] 2023-07-04 16:32:23,194 com.qiupeng.bean.di.BeanDiTest - 线程名称:Thread-0,testScopeModel=com.qiupeng.bean.ioc.model.ThreadModel@45f2bd18
[INFO ] 2023-07-04 16:32:23,194 com.qiupeng.bean.di.BeanDiTest - 线程名称:Thread-3,testScopeModel=com.qiupeng.bean.ioc.model.ThreadModel@4db785ed
[INFO ] 2023-07-04 16:32:23,194 com.qiupeng.bean.di.BeanDiTest - 线程名称:Thread-4,testScopeModel=com.qiupeng.bean.ioc.model.ThreadModel@1eca303
[INFO ] 2023-07-04 16:32:23,200 com.qiupeng.bean.di.BeanDiTest - 线程名称:Thread-0,testScopeModel=com.qiupeng.bean.ioc.model.ThreadModel@45f2bd18
[INFO ] 2023-07-04 16:32:23,200 com.qiupeng.bean.di.BeanDiTest - 线程名称:Thread-4,testScopeModel=com.qiupeng.bean.ioc.model.ThreadModel@1eca303
[INFO ] 2023-07-04 16:32:23,200 com.qiupeng.bean.di.BeanDiTest - 线程名称:Thread-2,testScopeModel=com.qiupeng.bean.ioc.model.ThreadModel@7882f6f8
[INFO ] 2023-07-04 16:32:23,200 com.qiupeng.bean.di.BeanDiTest - 线程名称:Thread-3,testScopeModel=com.qiupeng.bean.ioc.model.ThreadModel@4db785ed
[INFO ] 2023-07-04 16:32:23,200 com.qiupeng.bean.di.BeanDiTest - 线程名称:Thread-1,testScopeModel=com.qiupeng.bean.ioc.model.ThreadModel@3b6fcb57
[INFO ] 2023-07-04 16:32:23,200 com.qiupeng.bean.di.BeanDiTest - 线程名称:Thread-4,testScopeModel=com.qiupeng.bean.ioc.model.ThreadModel@1eca303
[INFO ] 2023-07-04 16:32:23,201 com.qiupeng.bean.di.BeanDiTest - 线程名称:Thread-2,testScopeModel=com.qiupeng.bean.ioc.model.ThreadModel@7882f6f8
[INFO ] 2023-07-04 16:32:23,201 com.qiupeng.bean.di.BeanDiTest - 线程名称:Thread-3,testScopeModel=com.qiupeng.bean.ioc.model.ThreadModel@4db785ed
[INFO ] 2023-07-04 16:32:23,201 com.qiupeng.bean.di.BeanDiTest - 线程名称:Thread-1,testScopeModel=com.qiupeng.bean.ioc.model.ThreadModel@3b6fcb57
[INFO ] 2023-07-04 16:32:23,201 com.qiupeng.bean.di.BeanDiTest - 线程名称:Thread-4,testScopeModel=com.qiupeng.bean.ioc.model.ThreadModel@1eca303
[INFO ] 2023-07-04 16:32:23,201 com.qiupeng.bean.di.BeanDiTest - 线程名称:Thread-2,testScopeModel=com.qiupeng.bean.ioc.model.ThreadModel@7882f6f8
[INFO ] 2023-07-04 16:32:23,201 com.qiupeng.bean.di.BeanDiTest - 线程名称:Thread-0,testScopeModel=com.qiupeng.bean.ioc.model.ThreadModel@45f2bd18
[INFO ] 2023-07-04 16:32:23,201 com.qiupeng.bean.di.BeanDiTest - 线程名称:Thread-4,testScopeModel=com.qiupeng.bean.ioc.model.ThreadModel@1eca303
[INFO ] 2023-07-04 16:32:23,201 com.qiupeng.bean.di.BeanDiTest - 线程名称:Thread-3,testScopeModel=com.qiupeng.bean.ioc.model.ThreadModel@4db785ed
[INFO ] 2023-07-04 16:32:23,201 com.qiupeng.bean.di.BeanDiTest - 线程名称:Thread-1,testScopeModel=com.qiupeng.bean.ioc.model.ThreadModel@3b6fcb57
[INFO ] 2023-07-04 16:32:23,201 com.qiupeng.bean.di.BeanDiTest - 线程名称:Thread-2,testScopeModel=com.qiupeng.bean.ioc.model.ThreadModel@7882f6f8
[INFO ] 2023-07-04 16:32:23,201 com.qiupeng.bean.di.BeanDiTest - 线程名称:Thread-0,testScopeModel=com.qiupeng.bean.ioc.model.ThreadModel@45f2bd18
[INFO ] 2023-07-04 16:32:23,201 com.qiupeng.bean.di.BeanDiTest - 线程名称:Thread-3,testScopeModel=com.qiupeng.bean.ioc.model.ThreadModel@4db785ed
[INFO ] 2023-07-04 16:32:23,201 com.qiupeng.bean.di.BeanDiTest - 线程名称:Thread-1,testScopeModel=com.qiupeng.bean.ioc.model.ThreadModel@3b6fcb57
[INFO ] 2023-07-04 16:32:23,202 com.qiupeng.bean.di.BeanDiTest - 线程名称:Thread-0,testScopeModel=com.qiupeng.bean.ioc.model.ThreadModel@45f2bd18
观察运行结果,可以发现通过线程中获取多次bean对象,返回的bean对象都是同一个。
4. 总结
- scope属性的主要作用是为了控制bean对象的作用域。
- 常用的scope属性有singleton、prototype、request、session、application、websocket。
- 自定义Scope需要实现scope接口并且将自定义的scope注入到spring中去。
5. 扩展知识
上面的自定义scope在xml中进行配置CustomScopeConfigurer实例,再将自定义的scope注入到该对象中去的,其实也可以使用程序来进行控制的,
例子:
public class ScopeTest {
private static final Logger logger = LogManager.getLogger(BeanDiTest.class);
GenericApplicationContext context;
@BeforeEach
public void testBefore() {
context = new GenericApplicationContext();
new XmlBeanDefinitionReader(context).loadBeanDefinitions("classpath:/bean/application-scope.xml");
//获取bean工厂对象
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
//注册自定义的scope
beanFactory.registerScope("threadScope", new ThreadScope());
context.refresh();
}
}