zoukankan      html  css  js  c++  java
  • 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("分隔线-----------------");
    
            }
        }
  • 相关阅读:
    流处理 —— Spark Streaming中的Window操作
    Spring框架参考手册(4.2.6版本)翻译——第三部分 核心技术 6.10.8 提供带注解的限定符元数据
    Spring框架参考手册(4.2.6版本)翻译——第三部分 核心技术 6.10.7 为自动检测组件提供作用域
    Spring框架参考手册(4.2.6版本)翻译——第三部分 核心技术 6.10.6 给自动检测组件命名
    Spring框架参考手册(4.2.6版本)翻译——第三部分 核心技术 6.10.5 在组件中定义bean的元数据
    Spring框架参考手册(4.2.6版本)翻译——第三部分 核心技术 6.10.4 使用过滤器自定义扫描
    Spring框架参考手册(4.2.6版本)翻译——第三部分 核心技术 6.10.3 自动检测类和注册bean的定义
    Spring框架参考手册(4.2.6版本)翻译——第三部分 核心技术 6.10.2 元注解
    Spring框架参考手册(4.2.6版本)翻译——第三部分 核心技术 6.10.1 @Component和深层的构造型注解
    Spring框架参考手册(4.2.6版本)翻译——第三部分 核心技术 6.10 类路径扫描和被管理的组件
  • 原文地址:https://www.cnblogs.com/8593l/p/12848843.html
Copyright © 2011-2022 走看看