Spring进阶(二)之Scope详解

应用中,有时候我们需要一个对象在整个应用中只有一个,有些对象希望每次使用的时候都重新创建一 个,spring对我们这种需求也提供了支持,在spring中这个叫做bean的作用域,xml中定义bean的时 候,可以通过scope属性指定bean的作用域

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

如: spring容器中scope常见的有5种

  • Singleton
  • Prototype
  • Request
  • Session
  • Application

想必大家都已经很熟悉了,这里就不做赘述了。下面我们要介绍的是自定义Scope

步骤

第1步:实现Scope接口

我们来看一下这个接口定义

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();
}

第2步:将自定义的scope注册到容器

需要调用org.springframework.beans.factory.config.ConfigurableBeanFactory#registerScope的方 法,看一下这个方法的声明

/**
* 向容器中注册自定义的Scope
*scopeName:作用域名称
* scope:作用域对象
**/
void registerScope(String scopeName, Scope scope);

 第3步:使用自定义的作用域

定义bean的时候,指定bean的scope属性为自定义的作用域名称。

案例

需求

下面我们来实现一个线程级别的bean作用域,同一个线程中同名的bean是同一个实例,不同的线程中 的bean是不同的实例

实现

/**
 * 自定义本地线程级别的bean作用域,不同的线程中对应的bean实例是不同的,同一个线程中同名的bean
 是同一个实例
 */
public class ThreadScope implements Scope {
    public static final String THREAD_SCOPE = "thread";//@1
    private ThreadLocal<Map<String, Object>> beanMap = new ThreadLocal() {
        @Override
        protected Object initialValue() {
            return new HashMap<>();
        }
    };
    @1:定义了作用域的名称为一个常量thread,可以在定义bean的时候给scope使用
            BeanScopeModel
    上面的构造方法中会输出当前线程的信息,到时候可以看到创建bean的线程。
    bean配置文件
    beans-thread.xml内容
    @Override
    public Object get(String name, ObjectFactory<?> objectFactory) {
        Object bean = beanMap.get().get(name);
        if (Objects.isNull(bean)) {
            bean = objectFactory.getObject();
            beanMap.get().put(name, bean);
        }
        return bean;
    }
    @Nullable
    @Override
    public Object remove(String name) {
        return this.beanMap.get().remove(name);
    }
    @Override
    public void registerDestructionCallback(String name, Runnable callback) {
//bean作用域范围结束的时候调用的方法,用于bean清理
        System.out.println(name);
    }
    @Nullable
    @Override
    public Object resolveContextualObject(String key) {
        return null;
    }
    @Nullable
    @Override
    public String getConversationId() {
        return Thread.currentThread().getName();
    }
}
public class BeanScopeModel {
    public BeanScopeModel(String beanScope) {
        System.out.println(String.format("线程:%s,create BeanScopeModel,
        {sope=%s},{this=%s}", Thread.currentThread(), beanScope, this));
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
    <!-- 自定义scope的bean -->
    <bean id="threadBean"
class="com.javacode2018.lesson001.demo4.BeanScopeModel" scope="thread">
        <constructor-arg index="0" value="thread"/>
    </bean>
</beans>
public class ThreadScopeTest {
    public static void main(String[] args) throws InterruptedException {
       String beanXml = "classpath:/com/javacode2018/lesson001/demo4/beansthread.xml";
       //手动创建容器
       ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(){
          @Override
          protected void postProcessBeanFactory(ConfigurableListableBeanFactorybeanFactory)                     
          {
                //向容器中注册自定义的scope
                beanFactory.registerScope(ThreadScope.THREAD_SCOPE, new ThreadScope());
                super.postProcessBeanFactory(beanFactory);
          }
       };
       //设置配置文件位置
       context.setConfigLocation(beanXml);
       //启动容器
       context.refresh();
       //使用容器获取bean
      for (int i = 0; i < 2; i++) {
          new Thread(() -> {
                System.out.println(Thread.currentThread() + "," +
            context.getBean("threadBean"));
                System.out.println(Thread.currentThread() + "," +
                context.getBean("threadBean"));
          }).start();
          TimeUnit.SECONDS.sleep(1);
      }
   }
}

总结

  • 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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

冰魄雕狼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值