• Spring第五篇:Spring bean的作用域


      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("分隔线-----------------");
    
            }
        }
  • 相关阅读:
    灵活的JavaScript(一)
    菜鸟快飞之JavaScript对象、原型、继承(三)
    菜鸟快飞之JavaScript对象、原型、继承(二)
    菜鸟快飞之JavaScript对象、原型、继承(一)
    undo清理 &redo 持久化
    mysql执行计划分析
    mysql5.6的统计信息
    pt-online-schema-change原理分析
    校验主从数据并修复
    使用伪master+binlog恢复数据
  • 原文地址:https://www.cnblogs.com/8593l/p/12848843.html
Copyright © 2020-2023  润新知