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("分隔线-----------------");

        }
    }

 

posted @ 2020-05-09 15:45  风缱云流  阅读(207)  评论(0编辑  收藏  举报