Spring Bean的作用域

news2024/11/27 6:30:50

1.写在前面

前面的博客我们已经介绍完Spring的依赖的查找来源,依赖注入的来源等等相关知识,今天我们继续来介绍Spring的Bean的作用域。

2.Spring Bean作用域

作用域

在这里插入图片描述

3.“singleton” Bean作用域

配置

在这里插入图片描述

4.“prototype” Bean作用域

配置

在这里插入图片描述

注意事项

  • Spring 容器没有办法管理 prototype Bean 的完整生命周期, 也没有办法记录实例的存在。 销毁回调方法将不会执行, 可以利用 BeanPostProcessor 进行清扫工作。

示例代码如下:

package org.learn.spring.bean.scope;

import org.learn.spring.ioc.overview.domain.User;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;

import java.util.Map;


// Bean的作用域示例
// 结论一:
// singleton Bean 无论是依赖注入和依赖查找都是同一个对象
// prototype Bean 无论是依赖注入和依赖查找每次都是新的对象

// 结论二:
// 如果注入的是集合类型对象,singleton Bean 和 prototype Bean 均只存在一个
// prototype Bean 有别于其他地方的依赖注入 prototype Bean

// 结论三:
// 无论是 singleton Bean 还是 prototype Bean 都会执行初始化方法回调
// 不过仅 singleton Bean 会执行销毁方法回调
public class BeanScopeDemo implements DisposableBean {

    @Bean
    // 默认singleton
    public static User singletonUser() {
        return createUser();
    }

    @Bean
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public static User prototypeUser() {
        return createUser();
    }

    @Autowired
    @Qualifier("singletonUser")
    private User singletonUser;

    @Autowired
    @Qualifier("singletonUser")
    private User singletonUser1;

    @Autowired
    @Qualifier("prototypeUser")
    private User prototypeUser;

    @Autowired
    @Qualifier("prototypeUser")
    private User prototypeUser1;

    @Autowired
    @Qualifier("prototypeUser")
    private User prototypeUser2;

    @Autowired
    private Map<String, User> users;

    @Autowired
    private ConfigurableListableBeanFactory beanFactory; // Resolvable Dependency

    private static User createUser() {
        User user = new User();
        user.setId(System.nanoTime());
        return user;
    }

    public static void main(String[] args) {
        // 创建BeanFactory的容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        // 注册Configuration Class 配置类 -> Spring Bean
        applicationContext.register(BeanScopeDemo.class);

        applicationContext.addBeanFactoryPostProcessor(beanFactory -> {
            beanFactory.addBeanPostProcessor(new BeanPostProcessor() {
                @Override
                public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
                    System.out.printf("%s Bean 名称:%s 在初始化回调...%n", bean.getClass().getName(), beanName);
                    return bean;
                }
            });
        });

        // 启动应用上下文
        applicationContext.refresh();

        scopedBeansByLookup(applicationContext);

        scopedBeansByInjection(applicationContext);

        // 显示的关闭spring应用上下文
        applicationContext.close();
    }

    private static void scopedBeansByLookup(AnnotationConfigApplicationContext applicationContext) {
        for (int i = 0; i < 3; i++) {
            // singletonUser 每次查找的都是共享的Bean
            User singletonUser = applicationContext.getBean("singletonUser", User.class);
            System.out.println("singletonUser = " + singletonUser);

            // prototypeUser 每次查找的都是生成新的Bean
            User prototypeUser = applicationContext.getBean("prototypeUser", User.class);
            System.out.println("prototypeUser = " + prototypeUser);
        }
    }

    private static void scopedBeansByInjection(AnnotationConfigApplicationContext applicationContext) {
        BeanScopeDemo beanScopeDemo = applicationContext.getBean(BeanScopeDemo.class);

        System.out.println("beanScopeDemo.singletonUser = " + beanScopeDemo.singletonUser);
        System.out.println("beanScopeDemo.singletonUser1 = " + beanScopeDemo.singletonUser1);

        System.out.println("beanScopeDemo.prototypeUser = " + beanScopeDemo.prototypeUser);
        System.out.println("beanScopeDemo.prototypeUser1 = " + beanScopeDemo.prototypeUser1);
        System.out.println("beanScopeDemo.prototypeUser2 = " + beanScopeDemo.prototypeUser2);

        System.out.println("beanScopeDemo.users = " + beanScopeDemo.users);
    }

    @Override
    public void destroy() throws Exception {

        System.out.println("当前BeanScopeDemo Bean 正在销毁中....");

        this.prototypeUser.destroy();
        this.prototypeUser1.destroy();
        this.prototypeUser2.destroy();

        for (Map.Entry<String, User> entry : this.users.entrySet()) {
            String beanName = entry.getKey();
            BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
            if (beanDefinition.isPrototype()) { // 如果当前Bean 是 prototype scope
                User user = entry.getValue();
                user.destroy();
            }
        }

        System.out.println("当前BeanScopeDemo Bean 正在销毁完成....");

    }
}

5.“request” Bean作用域

配置

  • XML - <bean class=” …” scope = “ request” />
  • Java 注解 - @RequestScope 或 @Scope(WebApplicationContext.SCOPE_REQUEST)

实现

  • API - RequestScope

每次返回给前端的对象都是不一样的,但是spring中生成的对象是同一个。

6.“session” Bean作用域

配置

  • XML - <bean class=” …” scope = “ session” />
  • Java 注解 - @SessionScope 或 @Scope(WebApplicationContext.SCOPE_SESSION)

实现

  • API - SessionScope

每次返回给前端的对象是一样的(sessionId是一样的时候)但是spring中生成的对象是同一个。

7.“application” Bean作用域

配置

  • XML - <bean class=” …” scope = “ application” />
  • Java 注解 - @ApplicationScope 或 @Scope(WebApplicationContext.SCOPE_APPLICATION)

实现

  • API - ServletContextScope

8.自定义 Bean 作用域

实现 Scope

  • org.springframework.beans.factory.config.Scope

注册 Scope

  • API - org.springframework.beans.factory.config.ConfigurableBeanFactory#registerScope

  • 配置

    <bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
    	<property name="scopes">
    		<map>
    			<entry key="...">
    			</entry>
    		</map>
    	</property>
    </bean>
    

示例代码如下:

package org.learn.spring.bean.scope;

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

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

// ThreadLocal 级别的 Scope
public class ThreadLocalScope implements Scope {

    public static final String SCOPE_NAME = "thread-local";

    private final NamedThreadLocal<Map<String, Object>> threadLocal = new NamedThreadLocal("thread-local-scope") {
        public Map<String, Object> initialValue() {
            return new HashMap<>();
        }
    };

    // 通过容器去取
    @Override
    public Object get(String name, ObjectFactory<?> objectFactory) {

        // 非空
        Map<String, Object> context = getContext();
        Object object = context.get(name);
        if (object == null) {
            object = objectFactory.getObject();
            context.put(name, object);
        }
        return object;
    }

    private Map<String, Object> getContext() {
        return threadLocal.get();
    }

    // 删除的时候调用
    @Override
    public Object remove(String name) {
        Map<String, Object> context = getContext();
        return context.remove(name);
    }

    // 注册一个销毁系统的回调
    @Override
    public void registerDestructionCallback(String name, Runnable callback) {
        // TODO
    }

    @Override
    public Object resolveContextualObject(String key) {
        Map<String, Object> context = getContext();
        return context.get(key);
    }

    // 返回一个会话的id
    @Override
    public String getConversationId() {
        Thread thread = Thread.currentThread();
        return String.valueOf(thread.getId());
    }
}

package org.learn.spring.bean.scope;

import org.learn.spring.ioc.overview.domain.User;
import org.springframework.aop.ThrowsAdvice;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;

// 自定义 Scope 示例
public class ThreadLocalScopeDemo {

    @Bean
    @Scope(ThreadLocalScope.SCOPE_NAME)
    public User user() {
        return createUser();
    }

    private static User createUser() {
        User user = new User();
        user.setId(System.nanoTime());
        return user;
    }

    public static void main(String[] args) {
        // 创建BeanFactory的容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        // 注册Configuration Class 配置类 -> Spring Bean
        applicationContext.register(ThreadLocalScopeDemo.class);

        applicationContext.addBeanFactoryPostProcessor(beanFactory -> {
            // 注册自定义Scope
            beanFactory.registerScope(ThreadLocalScope.SCOPE_NAME, new ThreadLocalScope());
        });

        // 启动应用上下文
        applicationContext.refresh();

        scopedBeansByLookup(applicationContext);

        applicationContext.close();
    }

    private static void scopedBeansByLookup(AnnotationConfigApplicationContext applicationContext) {

        for (int i = 0; i < 3; i++) {
            Thread thread = new Thread(() -> {
                User user = applicationContext.getBean("user", User.class);
                System.out.printf("[Thread id : %d] user = %s %n", Thread.currentThread().getId(), user);
            });

            // 启动线程
            thread.start();
            // 强制线程执行完成
            try {
                thread.join();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

运行的结果如下:

在这里插入图片描述

9.面试题

9.1 Spring 內建的 Bean 作用域有几种?

singleton、 prototype、 request、 session、 application 以及websocket

9.2 singleton Bean 是否在一个应用是唯一的?

否, singleton bean 仅在当前 Spring IoC 容器( BeanFactory) 中是单例对象。

9.3 application” Bean 是否被其他方案替代?

可以的, 实际上, “ application” Bean 与“ singleton” Bean 没有本质区别

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

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

相关文章

[Redis]-持久化方式

[Redis]-持久化方式 森格 | 2022年11月 本文是在学习Redis中&#xff0c;对Redis持久化的个人总结。 一、 持久化与Redis 1.1 什么是持久化 持久化是一种将程序数据在瞬时状态和持久状态间的转换机制&#xff0c;也就是把数据保存到可永久保存的存储设备中去。 1.2 Redis的持…

【Hack The Box】linux练习-- Writer

HTB 学习笔记 【Hack The Box】linux练习-- Writer &#x1f525;系列专栏&#xff1a;Hack The Box &#x1f389;欢迎关注&#x1f50e;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; &#x1f4c6;首发时间&#xff1a;&#x1f334;2022年11月27日&#x1f334; &#x1f3…

《web课程设计》使用HTML+CSS制作大学生校园二手交易网站

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

阿里P8现身说法,解密“架构”原理与实战笔记:从分布式到微服务

分布式架构与微服务平台是当今IT界的关键技术&#xff0c;也是资深软件工程师和系统架构师必须掌握的核心技术。 微服务、云原生、Kubernetes、Service Mesh是分布式领域的热点技术&#xff0c;它们并不是凭空出现的&#xff0c;一定继承了某些“前辈”的优点。我们不仅要了解…

十万部冷知识:足球踢进了观众席,观众能把球拿走吗?

在看足球比赛的时候&#xff0c;我们经常会看到球员一脚射门踢偏了&#xff0c;球被打飞的情况&#xff0c;这时候球可就奔着观众席去了。就是因为考虑到有这种情况的发生&#xff0c;在比赛之前&#xff0c;比赛组织者&#xff0c;都会提前准备很多的备用球&#xff0c;当球被…

R11.8-11.8-11.8-11.8-BABSL

R11.8-11.8-11.8-11.8-BABSL R11.8-11.8-11.8-11.8-BABSL哈威柱塞泵宁波秉圣&#xff0c;主要特点是高自吸转速&#xff0c;工作效率高&#xff0c;结构紧凑&#xff0c;工作压力高等。广泛用于压力机器具&#xff0c;测试和实验室设施&#xff0c;润滑装置等设备&#xff0c;在…

Home Assistant添加ESPHome设备(IO控制继电器)

文章目录1.在加载项商店中安装ESPHome2.添加设备2.1 编译并下载.bin到电脑2.2 下载固件到ESP82662.3 在Home Assistant添加并配置设备2.4 在设备与服务中配置3.再添加一个设备1.在加载项商店中安装ESPHome 点击配置-加载项 点击加载项商店 搜索ESPHome 2.添加设备 这里…

为什么我在公司里访问不了家里的电脑?

本文为掘金社区首发签约文章&#xff0c;14天内禁止转载&#xff0c;14天后未获授权禁止转载&#xff0c;侵权必究&#xff01; 上篇文章「为什么我们家里的IP都是192.168开头的&#xff1f;」提到&#xff0c;因为IPv4地址有限&#xff0c;最大42亿个。为了更好的利用这有限的…

前端学习一、准备工作

一、电脑 首先想学习前端肯定是需要一台电脑&#xff0c;配置方面目前市面上3千左右的电脑就差不多了&#xff0c;如果有能力的话肯定是越高越好&#xff0c;如何挑选笔记本可自行搜索&#xff0c;我在这方面不专业就不讲了。 二、安装软件 谷歌浏览器 如果无法访问谷歌浏览…

你知道MySQL是如何解决幻读的吗?

前言 SQL标准中定义了4种隔离级别&#xff0c;分别是读未提交、读已提交、可重复读以及序列化。不同的隔离级别下&#xff0c;可以解决不同的并发问题&#xff0c;如下图所示。当然MySQL也基本遵循了这个标准&#xff0c;但是在实现上稍有不同。 本文重点探讨下MySQL是如何解…

MySQL线程池

概述 池化技术&#xff0c;包括线程池、连接池、内存池、对象池等。作用就是提前保存大量的资源&#xff0c;或将用过的资源保存起来&#xff0c;等下一次需要使用该资源时再取出来重复使用。 线程池&#xff1a;通过预先创建一定数量的线程&#xff0c;当有请求达到时&#…

黑苹果之技嘉(GIGABYTE)主板BIOS设置篇

很多童鞋安装黑苹果的时候会卡住&#xff0c;大部分原因是cfg lock 没有关闭&#xff0c;以及USB端口或SATA模式设置错误。 为了避免这些安装阶段报错的情况发生&#xff0c;今天给大家分享一下超详细的BIOS防踩坑设置指南--技嘉&#xff08;GIGABYTE&#xff09;主板BIOS篇&am…

OpenGL官方文档中的入门教程源代码:在3维空间中自由移动

OpenGL官方文档中的入门教程源代码&#xff1a;在3维空间中自由移动项目总览&#xff1a;一、开发前的准备工作1.将以上链接中的三个文件分别放到自己硬盘的一个文件夹中&#xff1a;例如D盘/OpenGL/...2.打开VS2022创建一个项目&#xff0c;右击窗体选择属性3.配置这3个文件的…

SQL Server全套教程(基于SQL语句----预览版)

SQL Server全套教程全程干货1. 数据库的基础操作1.1.0 创建数据库1.1.1 查看及修改数据库1.1.3 分离、附加和删除数据库1.1.4 数据库的备份和还原2.数据库表的相关操作2.1.0 常用数据类型2.1.1 表结构的创建2.1.2 表结构的查看及修改2.1.3 表约束的创建2.1.4 表约束的修改2.1.5…

2013款别克凯越危险警告灯不亮故障诊断方案设计

目 录 一、预约与准备工作 1 &#xff08;一&#xff09;工作描述 1 &#xff08;二&#xff09;预约 1 &#xff08;三&#xff09;准备工作 1 1、分析故障可能原因 1 2、工具、量具准备 1 3、辅料准备 2 二、接车、问诊与制单 2 &#xff08;一&#xff09;接车、问诊 2 &am…

mysql笔记

幻读 概念 一个事务中的两次同样的查询不一致。 解决幻读&#xff1a; RR&#xff1a;使用select ... for update加排他锁 for update的引入是为了幂等性问题&#xff0c;如果不加for update可能出现并发问题。 【参考&#xff1a;MySQL幻读详解及解决方法_学而不思则忘的博…

Word处理控件Aspose.Words功能演示:从 Java 中的 Word 文档中提取图像

图像通常用于表示 Word 文档中的重要信息。在文本旁边包含图像使内容更具吸引力。在某些情况下&#xff0c;您可能需要以编程方式提取嵌入在 Word 文档中的图像。为此&#xff0c;本文介绍了如何使用 Java 从 Word 文档中提取图像。 Aspose.Words for . java 最新下载&#xf…

著名歌唱家大衣哥太豪横了,参加商演被主办方请到五星级酒店就餐

自从农民歌唱家大衣哥&#xff0c;被前好友谷传民起诉后&#xff0c;他的人气不降反升&#xff0c;各种商演邀约也都不断。就在前几天&#xff0c;农民歌唱家大衣哥在商演结束后&#xff0c;被主办方邀请到五星级大酒店&#xff0c;享受了一顿丰盛的晚餐。 作为普通老百姓来说&…

【场景化解决方案】北极星深度集成钉钉PaaS,让OKR管理更加敏捷高效

方案简介 北极星OKR作为一款企业数字化目标管理软件&#xff0c;致力于为企业客户提供专业高效的数字化系统和一站式服务支持&#xff0c;助力企业管理转型升级。如今通过与钉钉的深度融合&#xff0c;在信息的反馈与交互和团队的协作上&#xff0c;营造了更加敏捷的场景&…

leetcode93. 复原 IP 地址

文章目录题目思考代码和注释总结题目 有效 IP 地址 正好由四个整数&#xff08;每个整数位于 0 到 255 之间组成&#xff0c;且不能含有前导 0&#xff09;&#xff0c;整数之间用 ‘.’ 分隔。 例如&#xff1a;“0.1.2.201” 和 “192.168.1.1” 是 有效 IP 地址&#xff0…