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" />
application:一个web应用程序对应一个bean实例,通常情况下和singleton效果类似的,不过也有不一样的地方,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("分隔线-----------------"); } }