4.spring系列- bean的scope

先看下bean的作用域在xml的使用

语法:

<bean id="" class="" scope="作用域" /> 

singleton

当scope的值设置为singleton的时候,整个spring容器中只会存在一个bean实例,通过容器多次查找bean的时候(调用BeanFactory的getBean方法或者bean之间注入依赖的bean对象的时候),返回的都是同一个bean对象,singleton是scope的默认值,所以spring容器中默认创建的bean对象是单例的,通常spring容器在启动的时候,会将scope为singleton的bean创建好放在容器中(有个特殊的情况,当bean的lazy被设置为true的时候,表示懒加载,那么使用的时候才会创建),用的时候直接返回。

案例

bean xml 配置

    <bean id="helloSpring" class="com.spring.beanDemo.HelloSpring" scope="singleton">
        <constructor-arg index="0" value="singleton"/>
    </bean>

实体类:

public class HelloSpring {

    HelloSpring(String beanScope) {
        System.out.println(String.format("create HelloSpring,{sope=%s},{this=%s}", beanScope, this));
    }

    public void hello() {
        System.out.println("hello");
    }

}

测试:

@Test
    public void singletonBean() {
        String path = "classpath:/bean/beansScope.xml";
        System.out.println("spring 容器开始启动。。。");
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(path);
        System.out.println("spring 容器启动完毕。。。");
        System.out.println(context.getBean("helloSpring"));
        System.out.println(context.getBean("helloSpring"));
        System.out.println(context.getBean("helloSpring"));
    }

运行结果:

spring 容器开始启动。。。
10:56:25.170 [main] DEBUG org.springframework.context.support.ClassPathXmlApplicationContext - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@8e0379d
10:56:25.499 [main] DEBUG org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loaded 2 bean definitions from class path resource [bean/beansScope.xml]
10:56:25.609 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'helloSpring'
create HelloSpring,{sope=singleton},{this=com.spring.beanDemo.HelloSpring@41a0aa7d}
spring 容器启动完毕。。。
com.spring.beanDemo.HelloSpring@41a0aa7d
com.spring.beanDemo.HelloSpring@41a0aa7d
com.spring.beanDemo.HelloSpring@41a0aa7d

结论:单例bean的创建是在容器启动完毕之后,就已经创建完成,缓存在spring容器中。

单例bean使用注意

单例bean是整个应用共享的,所以需要考虑到线程安全问题,之前在玩springmvc的时候,springmvc中controller默认是单例的,有些开发者在controller中创建了一些变量,那么这些变量实际上就变成共享的了,controller可能会被很多线程同时访问,这些线程并发去修改controller中的共享变量,可能会出现数据错乱的问题;所以使用的时候需要特别注意。

prototype

如果scope被设置为prototype类型的了,表示这个bean是多例的,通过容器每次获取的bean都是不同的实例,每次获取都会重新创建一个bean实例对象。

案例

bean xml配置:

    <bean id="helloSpringPrototype" class="com.spring.beanDemo.HelloSpring" scope="prototype">
        <constructor-arg index="0" value="prototype"/>
    </bean>

实体类不变,我们测试代码如下:

@Test
    public void prototypeBean() {
        String path = "classpath:/bean/beansScope.xml";
        System.out.println("spring 容器开始启动。。。");
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(path);
        System.out.println("spring 容器启动完毕。。。");
        System.out.println(context.getBean("helloSpringPrototype"));
        System.out.println(context.getBean("helloSpringPrototype"));
        System.out.println(context.getBean("helloSpringPrototype"));
    }

测试结果:

spring 容器开始启动。。。
11:00:56.063 [main] DEBUG org.springframework.context.support.ClassPathXmlApplicationContext - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@8e0379d
11:00:56.378 [main] DEBUG org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loaded 1 bean definitions from class path resource [bean/beansScope.xml]
spring 容器启动完毕。。。
create HelloSpring,{sope=prototype},{this=com.spring.beanDemo.HelloSpring@6caf0677}
com.spring.beanDemo.HelloSpring@6caf0677
create HelloSpring,{sope=prototype},{this=com.spring.beanDemo.HelloSpring@413d1baf}
com.spring.beanDemo.HelloSpring@413d1baf
create HelloSpring,{sope=prototype},{this=com.spring.beanDemo.HelloSpring@16eb3ea3}
com.spring.beanDemo.HelloSpring@16eb3ea3

结论:spring在启动时,并没有创建bean,而是我们每次在去getbean的时候,去重新创建一个bean

request

当一个bean的作用域为request,表示在一次http请求中,一个bean对应一个实例;对每个http请求都会创建一个bean实例,request结束的时候,这个bean也就结束了,request作用域用在spring容器的web环境中。

session

这个和request类似,也是用在web环境中,session级别共享的bean,每个会话会对应一个bean实例,不同的session对应不同的bean实例

application

全局web应用级别的作用于,也是在web环境中使用的,一个web应用程序对应一个bean实例,通常情况下和singleton效果类似的,不过也有不一样的地方,singleton是每个spring容器中只有一个bean实例,一般我们的程序只有一个spring容器,但是,一个应用程序中可以创建多个spring容器,不同的容器中可以存在同名的bean,但是sope=aplication的时候,不管应用中有多少个spring容器,这个应用中同名的bean只有一个。

接下来我们说下自定义scope

自定义scope 3个步骤

1:实现Scope接口
2:将自定义的scope注册到容器,需要实现 ConfigurableBeanFactory#registerScope方法
3:使用自定义Scope

案例

需求:同一个线程中同名的bean是同一个实例,不同的线程中的bean是不同的实例。

分析:bean在线程中是共享的,所以我们通过ThreadLocal来实现,ThreadLocal可以实现线程中数据的共享。

第一步:实现Scope

public class MyScope implements Scope {

    private ThreadLocal<Map<String,Object>> threadLocal = new ThreadLocal(){
        @Override
        protected Object initialValue() {
            return new HashMap<String,Object>();
        }
    };

    @Override
    public Object get(String s, ObjectFactory<?> objectFactory) {
        Object obj = threadLocal.get().get(s);
        if(Objects.isNull(obj)){
            obj = objectFactory.getObject();
            threadLocal.get().put(s,obj);
        }
        return obj;
    }

    @Override
    public Object remove(String s) {
        return threadLocal.get().remove(s);
    }

    @Override
    public void registerDestructionCallback(String s, Runnable runnable) {

    }

    @Override
    public Object resolveContextualObject(String s) {
        System.out.println(s);
        return null;
    }

    @Override
    public String getConversationId() {
        return Thread.currentThread().getName();
    }
}

第二步:注册到容器中

		String path = "classpath:/bean/beansScope.xml";
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(path);
        context.getBeanFactory().registerScope("custom", new MyScope());

第三步:使用

建立一个bean

BeanScopeModel :

public class BeanScopeModel {

    private BeanScopeModel(String beanScope){
        System.out.println(String.format("线程:%s,create BeanScopeModel,{sope=%s},{this=%s}",
                Thread.currentThread(), beanScope, this));
    }
}

配置xml中bean的作用域:注意scope的设置,和注册到容器中的名字是一直的

    <bean id="beanScopeModel" class="com.spring.beanCustom.BeanScopeModel" scope="custom">
        <constructor-arg index="0" value="thread"/>
    </bean>

测试:

@Test
    public void myScopeBean() throws InterruptedException {
        String path = "classpath:/bean/beansScope.xml";
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(path);
        context.getBeanFactory().registerScope("custom", new MyScope());
        //使用容器获取bean
        for (int i = 0; i < 2; i++) { //@2
            new Thread(() -> {
                System.out.println(Thread.currentThread() + " --> " + context.getBean("beanScopeModel"));
                System.out.println(Thread.currentThread() + "," + context.getBean("beanScopeModel"));
            }).start();
            TimeUnit.SECONDS.sleep(1);
        }

    }

结果:

线程:Thread[Thread-0,5,main],create BeanScopeModel,{sope=thread},{this=com.spring.beanCustom.BeanScopeModel@5f7d99dd}
Thread[Thread-0,5,main] --> com.spring.beanCustom.BeanScopeModel@5f7d99dd
Thread[Thread-0,5,main],com.spring.beanCustom.BeanScopeModel@5f7d99dd
线程:Thread[Thread-1,5,main],create BeanScopeModel,{sope=thread},{this=com.spring.beanCustom.BeanScopeModel@c39f49}
Thread[Thread-1,5,main] --> com.spring.beanCustom.BeanScopeModel@c39f49
Thread[Thread-1,5,main],com.spring.beanCustom.BeanScopeModel@c39f49

每个线程获取的bean是一致的,不同线程,获取的bean是不一致的

总结

spring容器自带的有2种作用域,分别是singleton和prototype;还有3种分别是spring web容器环境中才支持的request、session、application

singleton是spring容器默认的作用域,一个spring容器中同名的bean实例只有一个,多次获取得到的是同一个bean;单例的bean需要考虑线程安全问题

prototype是多例的,每次从容器中获取同名的bean,都会重新创建一个;多例bean使用的时候需要考虑创建bean对性能的影响

一个应用中可以有多个spring容器

自定义scope 3个步骤,实现Scope接口,将实现类注册到spring容器,使用自定义的sope

参考链接:bean的scope

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值