1、spring bean的5种scope
2、自定义scope的实现
spring的bean的作用域可以在xml文件中定义bean的时候,通过配置scope属性指定。scope作用域的指定,可以支持我们在整个应用中只创建一个bean对象、或者每次获取bean实例的时候都创建一个、或者每次发起request的时候都创建一个等情况。
<bean id="" class="" scope="作用域" />
一、单例(singleton)
spring中默认bean的scope作用域是singleton(单例)。
当scope的值设置为singleton的时候,整个spring容器中只会存在一个bean实例,通过容器多次查找bean的时候(调用BeanFactory的getBean方法或者bean之间注入依赖的bean对象的时候),返回的都是同一个bean对象,通常spring容器在启动的时候,会将scope为singleton的bean创建好放在容器中(有个特殊的情况,当bean的lazy被设置为true的时候,表示懒加载,那么使用的时候才会创建),用的时候直接返回。
创建bean类:
public class SingeltonBeanModel {
public SingeltonBeanModel(String beanScope) {
System.out.println(String.format("create singeltonBean scope:{%s}", beanScope));
}
}
test方法运行结果,从结果中可以看出,spring容器创建单例bean的时候,在容器启动的时候就完成创建,并加入缓存。供其他调用。
spring容器准备启动
create singeltonBean scope:{singleton}
spring容器启动结束
bean单例获取
com.java.spring01.demo4.SingeltonBeanModel@a74868d
com.java.spring01.demo4.SingeltonBeanModel@a74868d
com.java.spring01.demo4.SingeltonBeanModel@a74868d
单例bean使用注意
单例bean是整个应用共享的,所以需要考虑到线程安全问题,springmvc中controller默认是单例的,有些开发者在controller中创建了一些变量,那么这些变量实际上就变成共享的了,controller可能会被很多线程同时访问,这些线程并发去修改controller中的共享变量,可能会出现数据错乱的问题,所以使用的时候需要特别注意。
二、多例(prototype)
bean为多例,从spring中获取对象的时候才会去创建对象。
多例test方法运行结果,从结果可以很明显的看出,从容器中获取对象的时候,才会去调用构造方法,创建bean实例。
bean多例获取
create singeltonBean scope:{prototype}
com.java.spring01.demo4.SingeltonBeanModel@12c8a2c0
create singeltonBean scope:{prototype}
com.java.spring01.demo4.SingeltonBeanModel@7e0e6aa2
create singeltonBean scope:{prototype}
com.java.spring01.demo4.SingeltonBeanModel@365185bd
多例bean使用注意
多例bean每次获取的时候都会重新创建,如果这个bean比较复杂,创建时间比较长,会影响系统的性能,这个地方需要注意。
三、request、session、application
request、session、application都是在spring web容器环境中才会有的。
request:表示在一次http请求中,一个bean对应一个实例;对每个http请求都会创建一个bean实例,request结束的时候,这个bean也就结束了。
<bean id="" class="" scope="request" />
session:session级别共享的bean,每个会话会对应一个bean实例,不同的session对应不同的bean实例。
<bean id="" class="" scope="session" />
singleton是每个spring容器中只有一个bean实例,一般我们的程序只有一个spring容器,但是,一个应用程序中可以创建多个spring容器,不同的容器中可以存在同名的bean,但是sope=aplication的时候,不管应用中有多少个spring容器,这个应用中同名的bean只有一个。
<bean id="" class="" scope="application" />
四、自定义scope
scope接口的源代码
package org.springframework.beans.factory.config;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.lang.Nullable;
public interface Scope {
/**
* 返回当前作用域中name对应的bean对象
* name:需要检索的bean的名称
* objectFactory:如果name对应的bean在当前作用域中没有找到,那么可以调用这个ObjectFactory来创建这个对象
**/
Object get(String name, ObjectFactory<?> objectFactory);
/**
* 将name对应的bean从当前作用域中移除
**/
@Nullable
Object remove(String name);
/**
* 用于注册销毁回调,如果想要销毁相应的对象,则由Spring容器注册相应的销毁回调,而由自定义作用域选择是不是要销毁相应的对象
*/
void registerDestructionCallback(String name, Runnable callback);
/**
* 用于解析相应的上下文数据,比如request作用域将返回request中的属性。
*/
@Nullable
Object resolveContextualObject(String key);
/**
* 作用域的会话标识,比如session作用域将是sessionId
*/
@Nullable
String getConversationId();
}
自定义scope的三个步骤
1、实现scope接口
package com.java.spring01.demo4;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.Scope;
import org.springframework.lang.NonNull;
import java.util.HashMap;
import java.util.Map;
public class ThreadScope implements Scope {
public static final String THREAD_SCOPE = "thread";
private final ThreadLocal<Map<String, Object>> threadScope = new ThreadLocal(){
@Override
public Map<String, Object> initialValue(){
return new HashMap<String, Object>();
}
};
@Override
public Object get(String name, ObjectFactory<?> objectFactory) {
Map<String, Object> map = this.threadScope.get();
Object bean = map.get(name);
if(null == bean){
bean = objectFactory.getObject();
map.put(name, bean);
}
return bean;
}
@Override
@NonNull
public Object remove(String name) {
Map<String, Object> map = this.threadScope.get();
return map.remove(name);
}
@Override
public void registerDestructionCallback(String name, Runnable runnable) {
System.out.println("threadScope 清理bean对象" + name);
}
@Override
public Object resolveContextualObject(String name) {
return null;
}
@Override
public String getConversationId() {
return Thread.currentThread().getName();
}
}
2、将自定义scope注册到spring容器中
在bean.xml文件中增加
<bean id="threadBean" class="com.java.spring01.demo4.SingeltonBeanModel" scope="thread">
<constructor-arg name="beanScope" value="thread"/>
</bean>
3、使用自定义,test方法。关键步骤在自定义容器的注册
@Test
public void threadTest() throws InterruptedException {
// conetxt = new ClassPathXmlApplicationContext();
// String beanxXML = "classpath:/spring01/demo4/bean.xml";
// conetxt.setConfigLocation(beanxXML);
// //启动容器
// conetxt.refresh();
conetxt.getBeanFactory().registerScope(ThreadScope.THREAD_SCOPE, new ThreadScope());
//获取多线程对象
for(int i = 0; i < 2; i++){
new Thread(() ->{
System.out.println(Thread.currentThread().getName() + "," + conetxt.getBean("threadBean"));
System.out.println(Thread.currentThread().getName() + "," + conetxt.getBean("threadBean"));
}).start();
TimeUnit.SECONDS.sleep(1);
System.out.println("分隔线-----------------");
}
}