《Spring使用到源码理解》第二章:Bean标签之Scope属性使用

1. 什么是Scope属性

上一章使用Bean标签中,已经尝试配置过Scope属性,比如将其设置成singleton表示该bean对象是个单例对象,在本章中将继续深入了解该属性。

2. 带着疑问阅读

  • Scope具体由什么作用?
  • 常见的Scope属性有那些?
  • 如何自定义Scope值?

3. 基础使用

先创建bean分别以singletonprototype来标识这两个java类。

public class SingletonModel{

}
public class PrototypeModel{

}

创建一个xml,将SigletonModelPrototypeModel注入到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
    输出结果
    bean容器中的对象
    通过调试程序可以看见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. 总结

  1. scope属性的主要作用是为了控制bean对象的作用域。
  2. 常用的scope属性有singletonprototyperequestsessionapplicationwebsocket
  3. 自定义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();
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值