bean scope——bean的作用域及注意点

时间:2022-07-22
本文章向大家介绍bean scope——bean的作用域及注意点,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

singleton

注意事项:
  1. 默认为singleton,即为单例。
  2. 整个应用共享,需考虑线程安全。
配置方式:
<bean id="" class="" scope="singleton" >或<bean id="" class="" >

prototype

注意事项:
  1. 需要在scope属性中指定prototype,多例。
  2. 每次获取时需重新加载。若实例较大,创建时间长、影响系统性能。
配置方式:
<bean id="" class="" scope="prototype">

以下为spring web容器环境中才有

request

注意事项:
  1. 需要在scope属性中指定request,每次请求创建。
  2. 由WebApplicationContext接口提供支持。
配置方式:
<bean id="" class="" scope="prototype">

session

注意事项:
  1. 需要在scope属性中指定session,每个session。
  2. 由WebApplicationContext接口提供支持。
  3. session级别共享
配置方式:
<bean id="" class="" scope="session">

application

注意事项:
  1. 需要在scope属性中指定application,每次请求创建。
  2. 由WebApplicationContext接口提供支持。
  3. 一般情况下与singleton相同,一个应用程序可以创建多个spring容器,scope为application时,同名bean一个。
配置方式:
<bean id="" class="" scope="application">

自定义scope

共三步

一、实现Scope接口

package org.springframework.beans.factory.config;

import org.springframework.beans.factory.ObjectFactory;

public interface Scope {
    /**
     * 返回当前作用域中name所对应的bean对象
     * @param name 需要检索的bean名称
     * @param objectFactory 没找到的情况下,创建该对象
     * @return 找到的对象
     */
    Object get(String name, ObjectFactory<?> objectFactory);

    /**
     * 移除检索到的name对象
     * @param name 需要检索的bean名称
     * @return 返回删除的对象
     */
    Object remove(String name);

    /**
     * 回调函数,用于销毁响应对象,
     * 由Spring容器注册相应的销毁回调
     * @param name  bean名称
     * @param callback 回调函数
     */
    void registerDestructionCallback(String name, Runnable callback);

    /**
     * 解析上下文数据
     * @param key 上下文键
     * @return 对应对象,若没找到,返回null
     */
    Object resolveContextualObject(String key);

    /**
     * 会话标识
     * @return 返回字符串会话标识
     */
    String getConversationId();

}

二、自定义scope注册到容器

方法声明片段如下:

package org.springframework.beans.factory.config;

public interface ConfigurableBeanFactory extends HierarchicalBeanFactory, SingletonBeanRegistry {
    //-------------一些代码--------------------------------    
    /**
     * 向容器注册作用域
     * @param scopeName 作用域名称
     * @param scope 作用域对象
     */
    void registerScope(String scopeName, Scope scope);
    //-------------一些代码--------------------------------
}

三、使用自定义scope

示例

ThreadScope

mainjavacomshimeathtestdemo1ThreadScope.java
package com.shimeath.test.demo1;

import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.Scope;
import org.springframework.lang.Nullable;

import java.lang.annotation.Annotation;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * TheadScope通过实现Scope接口,自定义scope
 * 作用域为一个线程
 */
public class ThreadScope implements Scope {
    /**
     * 作用域名称为thread,定义bean时可以给scope使用
     */
    public static final String THREAD_SCOPE = "thread";

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

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

    /**
     * 回调函数,作用域结束时调用,输出name
     * @param name
     * @param runnable
     */
    @Override
    public void registerDestructionCallback(String name, Runnable runnable) {
        System.out.println(name);
    }

    @Override
    public Object resolveContextualObject(String s) {
        return null;
    }

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

BeanScopeModel

mainjavacomshimeathtestdemo1BeanScopeModel.java
package com.shimeath.test.demo1;

public class BeanScopeModel {
    /**
     * 输出当前线程信息
     * @param beanScope
     */
    public BeanScopeModel(String beanScope){
        System.out.println(String.format("线程:%s,create BeanScopeModel,{scope=%s},{this=%s}", Thread.currentThread(), beanScope,this));
    }
}

bean配置

mainresourcesbean.xml
<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="threadBean" class="com.shimeath.test.demo1.BeanScopeModel" scope="thread">
        <constructor-arg index="0" value="thread"/>
    </bean>
</beans>

测试用例

package com.shimeath.test.demo1;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.util.Arrays;
import java.util.concurrent.TimeUnit;

public class Client {
    public static void main(String[] args) throws InterruptedException {
        //导入配置文件
        String beanXml = "classpath:bean.xml";

        //创建容器,加载bean配置文件
        ClassPathXmlApplicationContext context;
        context = new ClassPathXmlApplicationContext(beanXml);
        context.refresh();

        context.getBeanFactory().registerScope(ThreadScope.THREAD_SCOPE, new ThreadScope());

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

输出如下

线程:Thread[Thread-1,5,main],create BeanScopeModel,{scope=thread},{this=com.shimeath.test.demo1.BeanScopeModel@770a131f}
Thread[Thread-1,5,main],com.shimeath.test.demo1.BeanScopeModel@770a131f
Thread[Thread-1,5,main],com.shimeath.test.demo1.BeanScopeModel@770a131f
线程:Thread[Thread-2,5,main],create BeanScopeModel,{scope=thread},{this=com.shimeath.test.demo1.BeanScopeModel@a38feba}
Thread[Thread-2,5,main],com.shimeath.test.demo1.BeanScopeModel@a38feba
Thread[Thread-2,5,main],com.shimeath.test.demo1.BeanScopeModel@a38feba

总结

以上结果可以看出,自定义scope生效。不同的线程中的bean均为不同的bean