使用@Scope注解设置组件的作用域

news2025/2/23 14:39:00

前言

Spring容器中的组件默认是单例的,在Spring启动时就会实例化并初始化这些对象,并将其放到Spring容器中,之后,每次获取对象时,直接从Spring容器中获取,而不再创建对象。

1.@Scope注解概述

@Scope注解能够设置组件的作用域,Scope注解类的源码,如下所示

public @interface Scope {

	/**
	 * Alias for {@link #scopeName}.
	 * @see #scopeName
	 */
	@AliasFor("scopeName")
	String value() default "";

	/**
	 * Specifies the name of the scope to use for the annotated component/bean.
	 * <p>Defaults to an empty string ({@code ""}) which implies
	 * {@link ConfigurableBeanFactory#SCOPE_SINGLETON SCOPE_SINGLETON}.
	 * @since 4.2
	 * @see ConfigurableBeanFactory#SCOPE_PROTOTYPE
	 * @see ConfigurableBeanFactory#SCOPE_SINGLETON
	 * @see org.springframework.web.context.WebApplicationContext#SCOPE_REQUEST
	 * @see org.springframework.web.context.WebApplicationContext#SCOPE_SESSION
	 * @see #value
	 */
	@AliasFor("value")
	String scopeName() default "";

	ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT;

}

从**@Scope**注解类的源码中可以看出,在@Scope注解中可以设置如下值:

  1. ConfigurableBeanFactory#SCOPE_PROTOTYPE
  2. ConfigurableBeanFactory#SCOPE_SINGLETON
  3. org.springframework.web.context.WebApplicationContext#SCOPE_REQUEST
  4. org.springframework.web.context.WebApplicationContext#SCOPE_SESSION

查看一下ConfigurableBeanFactory接口的源码,发现在该接口中存在两个常量的定义

public interface ConfigurableBeanFactory extends HierarchicalBeanFactory, SingletonBeanRegistry {

	String SCOPE_SINGLETON = "singleton";


	String SCOPE_PROTOTYPE = "prototype";
}

SCOPE_SINGLETON就是singleton,而SCOPE_PROTOTYPE就是prototype

而SCOPE_REQUEST的值就是request,SCOPE_SESSION的值就是session

备注:request和session作用域是需要Web环境来支持的,这两个值基本上使用不到,实例对象的作用域设置为request和session

request.setAttribute("key", object);

session.setAttribute("key", object);

2.单实例bean作用域

创建MainConfig02配置类,实例化一个Person对象,并将其放置在Spring容器中

package com.tianxia.springannotation.config;

import com.tianxia.springannotation.entity.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 配置类
 * @author liqb
 * @date 2023-04-23 9:45
 **/
@Configuration
public class MainConfig02 {

    /**
     * 创建person实例
     * @author liqb
     * @date 2023-04-23 09:46
     * @return
     */
    @Bean("person02")
    public Person person() {
        return new Person("liqb", 24);
    }
}

编写测试方法,从Spring容器中按照id获取两个Person对象,并判断这两个对象是否是同一个对象

/**
 * 测试单实例bean
 * @author liqb
 * @date 2023-04-23 09:47
 */
@Test
public void testSingleBean() {
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig02.class);

    // 获取到的这个Person对象默认是单实例的,因为在IOC容器中给我们加的这些组件默认都是单实例的,
    // 所以说在这儿我们无论多少次获取,获取到的都是我们之前new的那个实例对象
    Person person = (Person) applicationContext.getBean("person02");
    Person person2 = (Person) applicationContext.getBean("person02");
    System.out.println(person == person2);
}

测试结果:true

结论:对象在Spring容器中默认是单实例的,Spring容器在启动时就会将实例对象加载到Spring容器中,之后,每次从Spring容器中获取实例对象,都是直接将对象返回,而不必再创建新的实例对象了。

2.多实例bean作用域

将MainConfig02配置类中Person对象的作用域修改成prototype,如下所示。

package com.tianxia.springannotation.config;

import com.tianxia.springannotation.entity.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;

/**
 * 配置类
 * @author liqb
 * @date 2023-04-23 9:45
 **/
@Configuration
public class MainConfig02 {

    /**
     * 创建person实例
     * @author liqb
     * @date 2023-04-23 09:46
     * @return
     */
    @Bean("person02")
    // 通过@Scope注解来指定该bean的作用范围,也可以说成是调整作用域
    @Scope("prototype")
    public Person person() {
        return new Person("liqb", 24);
    }
}

再次运行测试方法

测试结果:false

结论:当对象是多实例时,每次从Spring容器中获取对象时,都会创建新的实例对象,并且每个实例对象都不相等

3.单实例bean作用域何时创建对象

package com.tianxia.springannotation.config;

import com.tianxia.springannotation.entity.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 配置类
 * @author liqb
 * @date 2023-04-23 9:45
 **/
@Configuration
public class MainConfig02 {

    /**
     * 创建person实例
     * @author liqb
     * @date 2023-04-23 09:46
     * @return
     */
    @Bean("person02")
    public Person person() {
        System.out.println("给容器中添加咱们这个Person对象...");
        return new Person("liqb", 24);
    }
}

创建测试方法

/**
 * 测试单实例bean何时创建对象
 * @author liqb
 * @date 2023-04-23 10:01
 */
@Test
public void testSingleBeanTime() {
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig02.class);
}

输出的结果信息如下所示

在这里插入图片描述

结论:Spring容器在创建的时候,就将@Scope注解标注为singleton的组件进行了实例化,并加载到了Spring容器中。

/**
 * 测试单实例bean何时创建对象
 * @author liqb
 * @date 2023-04-23 10:01
 */
@Test
public void testSingleBeanTime() {
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig02.class);

    Person person = (Person) applicationContext.getBean("person02");
    Person person2 = (Person) applicationContext.getBean("person02");
    System.out.println(person == person2);
}

输出的结果信息如下所示

在这里插入图片描述

结论:Spring容器在启动时,将单实例组件实例化之后,会即刻加载到Spring容器中,以后每次从容器中获取组件实例对象时,都是直接返回相应的对象,而不必再创建新的对象了。

4.多实例bean作用域何时创建对象?

package com.tianxia.springannotation.config;

import com.tianxia.springannotation.entity.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;

/**
 * 配置类
 * @author liqb
 * @date 2023-04-23 9:45
 **/
@Configuration
public class MainConfig02 {

    /**
     * 创建person实例
     * @author liqb
     * @date 2023-04-23 09:46
     * @return
     */
    @Bean("person02")
    //通过@Scope注解来指定该bean的作用范围,也可以说成是调整作用域
    @Scope("prototype")
    public Person person() {
        System.out.println("给容器中添加咱们这个Person对象...");
        return new Person("liqb", 24);
    }
}

创建测试方法

/**
 * 测试多实例bean何时创建对象
 * @author liqb
 * @date 2023-04-23 10:01
 */
@Test
public void testPrototypeBeanTime() {
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig02.class);
}

输出的结果信息如下所示

在这里插入图片描述

结论:当向Spring容器中获取Person实例对象时,Spring容器才会实例化Person对象,再将其加载到Spring容器中去。

/**
 * 测试多实例bean何时创建对象
 * @author liqb
 * @date 2023-04-23 10:01
 */
@Test
public void testPrototypeBeanTime() {
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig02.class);

    Person person = (Person) applicationContext.getBean("person02");
    Person person2 = (Person) applicationContext.getBean("person02");
    System.out.println(person == person2);
}

输出的结果信息如下所示

在这里插入图片描述

结论:当对象的Scope作用域为多实例时,每次向Spring容器获取对象时,它都会创建一个新的对象并返回。很显然,以上获取到的person和person2就不是同一个对象了,每次从Spring容器中获取对象时,都会创建新的实例对象,并且每个实例对象都不相等。

5.单实例bean注意的事项

单实例bean是整个应用所共享的,所以需要考虑到线程安全问题,在玩SpringMVC的时候,SpringMVC中的Controller默认是单例的,有

些开发者在Controller中创建了一些变量,那么这些变量实际上就变成共享的了,Controller又可能会被很多线程同时访问,这些线程并发去修

改Controller中的共享变量,此时很有可能会出现数据错乱的问题,所以使用的时候需要特别注意。

6.多实例bean注意的事项

多实例bean每次获取的时候都会重新创建,如果这个bean比较复杂,创建时间比较长,那么就会影响系统的性能,因此这个地方需要注意点。

7.自定义Scope

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

package com.tianxia.springannotation.config.configEntity;

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

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * 自定义scope
 * @author liqb
 * @date 2023-04-23 10:26
 **/
public class CustomerScope implements Scope {

    public static final String CUSTOMER_SCOPE = "thread";

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

    /**
     * 返回当前作用域中name对应的bean对象
     * @author liqb
     * @date 2023-04-23 10:29
     * @param name 需要检索的bean对象的名称
     * @param objectFactory 如果name对应的bean对象在当前作用域中没有找到,那么可以调用这个objectFactory来创建这个bean对象
     * @return
     */
    @Override
    public Object get(String name, ObjectFactory<?> objectFactory) {
        // 获取bean对象
        Object bean = beanMap.get().get(name);
        if (Objects.isNull(bean)) {
            bean = objectFactory.getObject();
            beanMap.get().put(name, bean);
        }
        return bean;
    }

    /**
     * 将name对应的bean对象从当前作用域中移除
     * @author liqb
     * @date 2023-04-23 10:43
     * @param name bean名称
     * @return
     */
    @Override
    public Object remove(String name) {
        return this.beanMap.get().remove(name);
    }

    /**
     * 用于注册销毁回调,若想要销毁相应的对象,则由Spring容器注册相应的销毁回调,而由自定义作用域选择是不是要销毁相应的对象
     * @param name bean名称
     * @param callback 回调方法
     */
    @Override
    public void registerDestructionCallback(String name, Runnable callback) {
        System.out.println(name);
    }

    /**
     * 用于解析相应的上下文数据,比如request作用域将返回request中的属性
     * @author liqb
     * @date 2023-04-23 10:44
     * @param key
     * @return
     */
    @Override
    public Object resolveContextualObject(String key) {
        return null;
    }

    /**
     * 作用域的会话标识,比如session作用域的会话标识是sessionId
     * @author liqb
     * @date 2023-04-23 10:44
     * @return
     */
    @Override
    public String getConversationId() {
        return Thread.currentThread().getName();
    }
}

创建一个配置类

package com.tianxia.springannotation.config;

import com.tianxia.springannotation.config.configEntity.CustomerScope;
import com.tianxia.springannotation.entity.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;

/**
 * 配置类
 * @author liqb
 * @date 2023-04-23 10:45
 **/
@Configuration
public class MainConfig03 {

    /**
     * 创建person实例
     * @author liqb
     * @date 2023-04-23 09:46
     * @return
     */
    @Bean("person03")
    @Scope(CustomerScope.CUSTOMER_SCOPE)
    public Person person() {
        System.out.println("给容器中添加咱们这个Person对象...");
        return new Person("liqb", 24);
    }
}

编写测试方法

/**
 * 测试自定义Scope
 * @author liqb
 * @date 2023-04-23 10:47
 */
@Test
public void testCustomScope() {
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig03.class);

    // 向容器中注册自定义的Scope
    applicationContext.getBeanFactory().registerScope(CustomerScope.CUSTOMER_SCOPE, new CustomerScope());

    // 使用容器获取bean
    for (int i = 0; i < 2; i++) {
        new Thread(() -> {
            System.out.println(Thread.currentThread() + "," + applicationContext.getBean("person03"));
            System.out.println(Thread.currentThread() + "," + applicationContext.getBean("person03"));
        }).start();
    }
    try {
        TimeUnit.SECONDS.sleep(1);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

实验结果
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/450362.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【uni-app】【01】底部导航栏与页面切换

1.(配置文件在哪)uni-app 路由控制是在 pages.json文件中的。 2.(基本配置项有哪些)初学的时候主要有三个配置项&#xff0c;①pages ② globalStyle ③ tabbar [!TOC] 接下来主要是对这三个配置项做一个简单介绍。 pages 负责页面管理。不需要自己写的&#xff0c;你在项目的p…

【Scala入门】scala基础语法:类和对象,变量和常量

上一篇请移步【Scala入门】Scala下载及安装&#xff08;Windows&#xff09;以及Idea创建第一个scala项目_水w的博客-CSDN博客 目录 一、Scala 二、Scala基础语法 2.1 注释与标识符规范 2.2 变量与常量 【案例&#xff1a;变量声明和赋值】 2.3 object 【案例&#xff1…

合并二叉树-递归法

1题目 给你两棵二叉树&#xff1a; root1 和 root2 。 想象一下&#xff0c;当你将其中一棵覆盖到另一棵之上时&#xff0c;两棵树上的一些节点将会重叠&#xff08;而另一些不会&#xff09;。你需要将这两棵树合并成一棵新二叉树。合并的规则是&#xff1a;如果两个节点重叠…

gdb调试常用指令及案例讲解

文章目录 前言一、常用指令二、案例说明1、测试源文件2、编译和调试 三、其他指令四、案例说明 前言 GDB是一个由GNU开源组织发布的、UNIX/LINUX 操作系统下的、基于命令行的、功能强大的程序调试工具。 GDB 支持断点、单步执行、打印变量、观察变量、查看寄存器、查看堆栈等调…

【JavaEE】_2.文件与IO

目录 1.文件概述 1.1 文件的概念 1.2 文件的存储 1.3 文件的分类 1.4 目录结构 1.5 文件操作 1.5.1 文件系统操作 1.5.2 文件内容操作 2. Java文件系统操作 2.1 File类所处的包 2.2 构造方法 2.3 方法 2.3.1 与文件路径、文件名有关的方法 2.3.2 文件是否存在与普…

Java核心技术 卷1-总结-13

Java核心技术 卷1-总结-13 具体的集合散列集树集队列与双端队列优先级队列 映射基本映射操作 具体的集合 散列集 链表和数组可以有序的存储元素。但是&#xff0c;如果想要查看某个指定的元素&#xff0c;却又忘记了它的位置&#xff0c;就需要访问所有元素&#xff0c;直到找…

vue2数据响应式原理(5) 通过重写函数实现数组响应式监听

其实 我们之前对数组的一个监听 还并不是很完美 我们打开案例 打开 output.js 更改代码如下 import { observe } from "./dataResp" const output () > {var obj {data: {data: {map: {dom: {isgin: true}},arg: 13},name: "小猫猫"},bool: [1,2,3,4…

【经验与Bug】tensorflow草记

文章目录 1 常用小知识2 Learn1) 疑惑未解2) 为何要有"bias"&#xff1f; 3 问题处理1) jupyter的环境指定目录运行jupyter 2) Keras版本3) 为什么accuracy为100%&#xff0c;迭代时参数还在更新&#xff1f; 1 常用小知识 conda activate tf 在anaconda prompt使用&…

Android studio 播放音频文件 播放语速

一、使用 public class MainActivity extends AppCompatActivity {private Hsvolume mHsVolume null;Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mHsVolume new Hsvolume(th…

【YOLO系列】YOLOv1论文笔记

论文链接&#xff1a;[1506.02640] You Only Look Once: Unified, Real-Time Object Detection (arxiv.org) YOLO将目标检测看作回归问题&#xff0c;使用单个神经网络直接从完整图像上预测边界框和类别概率。&#xff08;端到端&#xff1a;输入原始数据&#xff0c;输出的是最…

E5EAA HENF105240R1将用于工业生产过程的测量、控制和管理

​E5EAA HENF105240R1将用于工业生产过程的测量、控制和管理 工业控制计算机是工业自动化控制系统的核心设备 工业控制计算机是工业自动化设备和信息产业基础设备的核心。传统意义上&#xff0c;将用于工业生产过程的测量、控制和管理的计算机统称为工业控制计算机&#xff0c;…

SpringBoot整合WebSocket的两种方式及微服务网关Gateway配置

一、说明 项目中后台微服务需要向前端页面推送消息&#xff0c;因此不可避免的需要用到WebSocket技术。SpringBoot已经为WebSocket的集成提供了很多支持&#xff0c;只是WebSocket消息如何通过微服务网关Spring Cloud Gateway向外暴露接口&#xff0c;实际开发过程中遇到了很多…

【数据结构第四章】- 串的模式匹配算法(BF 算法和 KMP 算法/用 C 语言实现)

目录 一、前言 二、BF 算法 三、KMP 算法 3.2.1 - KMP 算法的原理 3.2.2 - KMP 算法的实现 3.2.3 - KMP 算法的优化 创作不易&#xff0c;可以点点赞&#xff0c;如果能关注一下博主就更好了~ 一、前言 子串的定位运算通常称为串的模式匹配或串匹配。此运算的应用非常广…

美国主机的带宽和网络速度究竟有多快?

在选择一个主机时&#xff0c;其带宽和网络速度是非常重要的考虑因素。而美国主机在带宽和网络速度方面有着明显的优势&#xff0c;成为了众多用户的首选。那么&#xff0c;美国主机的带宽和网络速度究竟有多快呢?本文将通过分析美国主机的网络基础设施和数据中心设施&#xf…

golang入门项目——打卡抽奖系统

功能介绍 用户加入群组之后&#xff0c;会在签到群组所设的签到地点进行签到和签退&#xff0c;并限制同一个设备只能签到一个用户&#xff0c;签到成功之后。会获取一定的限制在该群组使用的积分。该群组可以设置一些抽奖活动&#xff0c;用户可使用该群组内的积分来进行该群…

Python+mysql+php搭建另类免费代理池

文章目录 前言:思路&#xff1a;开干&#xff1a;php连接MySQL取ip和端口&#xff1a;效果图&#xff1a; 最后调用代理池&#xff1a;总结&#xff1a; 前言: 为什么说另类的&#xff0c;因为我完全是按照我自己的想法来的&#xff0c;比较鸡肋&#xff0c;但是能用&#xff…

短视频app开发:如何提高视频播放稳定性

简介 如今&#xff0c;短视频已经成为人们日常生活中不可或缺的一部分&#xff0c;而短视频app的开发也日益成为了人们热议的话题。在短视频app开发的过程中&#xff0c;如何提高视频播放稳定性是一个非常重要的问题。本文将从短视频源码角度出发&#xff0c;分享提高短视频ap…

如何优化语音交友app开发的搜索和匹配算法

语音交友app开发的挑战 在当今社交媒体行业中&#xff0c;语音交友app开发已经成为一个热门的领域。越来越多的人开始使用语音交友app来寻找新的朋友&#xff0c;这也为开发者们带来了许多机会。然而&#xff0c;这个领域也面临着一些挑战。其中一个最大的挑战是如何优化搜索和…

掏空腰包,日子难过,机缘转岗软件测试,这100个日夜的心酸只有自己知道...

我今年27岁&#xff0c;原本从事着土木工程相关的工作&#xff0c;19年开始有了转行的想法... 大学刚毕业那年&#xff0c;我由于学的是土木工程专业&#xff0c;自然而然的从事了和土木工程相关的工作&#xff0c;房贷、车贷&#xff0c;在经济的高压下&#xff0c;当代社会许…

大数据题目测试(一)

目录 一、环境要求 二、提交结果要求 三、数据描述 四、功能要求 1.数据准备 2.使用 Spark&#xff0c;加载 HDFS 文件系统 meituan_waimai_meishi.csv 文件&#xff0c;并分别使用 RDD和 Spark SQL 完成以下分析&#xff08;不用考虑数据去重&#xff09;。 (1)配置环境…