当前位置: 首页 > news >正文

Spring学习(四):Scope的介绍及其失效解决方案

目录

一、spring当中有哪些scope

二、scope初始化与销毁演示

2.1 scope的初始化 

2.2 scope的销毁

三、scope失效及其解决方案 

3.1 scope失效演示

3.2 scope失效解决方案一:@Lazy 

3.3 scope失效解决方案二:设置proxyMode属性

3.4 scope失效解决方案三:ObjectFactory

3.5  scope失效解决方案四:ApplicationContext容器

3.6 总结


 

一、spring当中有哪些scope

        我使用的spring版本是5.3.10,其中scope有五种:

  • singleton:从spring中获取bean时,每次获取的是同一个bean

  • prototype:从spring中获取bean时,每次获取的是新的bean

  • request:生成的bean存活于request域中,生命周期也与request相同

  • session:生成的bean存活于session域中,生命周期也与session相同

  • application:生成的bean存活于application域中,生命周期也与application相同(spring中的application指的是servletContext)

二、scope初始化与销毁演示

2.1 scope的初始化 

        主启动类:

@SpringBootApplication
public class A08 {public static void main(String[] args) {SpringApplication.run(A08.class, args);}
}

        主启动类启动后,就会扫描到下面的三个bean:

@Scope("request")
@Component
public class BeanForRequest {private static final Logger log = LoggerFactory.getLogger(BeanForRequest.class);@PreDestroypublic void destroy() {log.debug("destroy");}
}
@Scope("session")
@Component
public class BeanForSession {private static final Logger log = LoggerFactory.getLogger(BeanForSession.class);@PreDestroypublic void destroy() {log.debug("destroy");}
}
@Scope("application")
@Component
public class BeanForApplication {private static final Logger log = LoggerFactory.getLogger(BeanForApplication.class);@PreDestroypublic void destroy() {log.debug("destroy");}
}

        这三个bean在controller中被用到:

@RestController
public class MyController {//MyController是单例,单例去使用其他的域,都要加@Lazy,否则会失效@Lazy@Autowiredprivate BeanForRequest beanForRequest;@Lazy@Autowiredprivate BeanForSession beanForSession;@Lazy@Autowiredprivate BeanForApplication beanForApplication;@GetMapping(value = "/test", produces = "text/html")public String test(HttpServletRequest request, HttpSession session) {ServletContext sc = request.getServletContext();String sb = "<ul>" +"<li>" + "request scope:" + beanForRequest + "</li>" +"<li>" + "session scope:" + beanForSession + "</li>" +"<li>" + "application scope:" + beanForApplication + "</li>" +"</ul>";return sb;}
}

        打开不同的浏览器, 刷新 http://localhost:8080/test 即可查看效果。如果 jdk > 8, 运行时请添加 --add-opens java.base/java.lang=ALL-UNNAMED

        打开一个浏览器(例如:Chrome)可以发现,每次刷新都可以看到request域的bean不一样,说明每次创建的是新的bean,request的bean会在请求结束后销毁。

        再打开一个浏览器(例如:edge)可以发现,会话域的bean也不同,是因为每个浏览器都会创建自己的会话。

         打开不同的浏览器也能看到,request和session的bean不相同,是因为每个浏览器都有自己的请求和会话域;而application的bean时相同的,因为他们对应的是同一个web应用程序

2.2 scope的销毁

  • singleton,容器启动时创建(未设置延迟),容器关闭时销毁

  • prototype,每次使用时创建,不会自动销毁,需要调用DefaultListableBeanFactory.destroyBean(bean) 销毁

  • request,每次请求用到此 bean 时创建,请求结束时销毁

  • session,每个会话用到此 bean 时创建,会话结束时销毁

  • application,web 容器用到此 bean 时创建,容器停止时销毁

        在浏览器中每次刷新我们都可以看到控制台打印出了beanForRequest的销毁方法,这是因为每次刷新都代表着一个请求结束了;session销毁时间默认为30min,为了方便演示,我们在application.properties文件中设置

server.servlet.session.timeout=10s

         这样session就可以在1min后自动销毁了(spring内部设置session销毁时间低于1min就视为1min,这篇文章剖析了这部分源码:SpringBoot设置Session失效时间 - 简书),我们可以在控制台上看到session销毁的日志

image-20220402144856449 

三、scope失效及其解决方案 

3.1 scope失效演示

        以单例注入多例为例(单例注入其他类型的scope也是同理),先看看主启动类:

@ComponentScan("com.itheima.a08.sub")
public class A08_1 {private static final Logger log = LoggerFactory.getLogger(A08_1.class);public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(A08_1.class);E e = context.getBean(E.class);log.debug("{}", e.getF1().getClass());log.debug("{}", e.getF1());log.debug("{}", e.getF1());log.debug("{}", e.getF1());context.close();}
}

        在主启动类中,我们先通过getBean方法拿到E的对象,再通过get方法拿到e中注入的f1对象。E并没有加@prototype注解,所以默认是个单例,而f1是个多例。

@Component
public class E {@Autowiredprivate F1 f1;public F1 getF1() {return f1;}
}
@Scope("prototype")
@Component
public class F1 {
}

        那么问题来了,我们在主启动类中多调用几次,每次返回的f1对象是同一个对象还是不同对象?我们既然将F1标注为多例,自然是期望返回不同对象的。

        然而运行可得,每次返回的都是同一对象 :

         原因是:对于单例对象来讲,依赖注入仅发生一次,后续再没有发生依赖注入,E再没有用到多例的F1,因此E用的始终是第一次依赖注入的F1。

3.2 scope失效解决方案一:@Lazy 

        我们在成员变量或者使用的方法上加上@Lazy注解,@Lazy能够帮助我们生成代理对象,这样,代理对象虽然还是同一个,但当每次使用代理对象的任意方法时,由代理创建新的f1对象。 

@Component
public class E {@Lazy@Autowiredprivate F1 f1;public F1 getF1() {return f1;}
}

        运行发现每次生成的bean已经不一样了。我们也打印一下e.getF1().getClass,看看生成的F1的真实类型,可以看到并不是F1,而是代理增强的类。

3.3 scope失效解决方案二:设置proxyMode属性

        我们还可以在被调用的多例类上设置 proxyMode 属性,我们用F2来演示这个解决办法:

@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
@Component
public class F2 {
}

        这个方法的本质也是生成代理。运行后可以看到,生成的bean也不一样了,获取到的F2的真实类型也是代理增强的类。 

3.4 scope失效解决方案三:ObjectFactory

        我们可以在E中注入的时候将F声明为 ObjectFactory 类型,这样每次获取的时候都由工厂生成不同的F。我们用F3来演示这个解决办法:

@Component
public class E {@Autowiredprivate ObjectFactory<F3> f3;public F3 getF3() {return f3.getObject();}
}
@Scope("prototype")
@Component
public class F3 {
}

3.5  scope失效解决方案四:ApplicationContext容器

        直接实现容器,利用容器的getBean方法来获取F多例bean。我们用F4来演示:

@Component
public class E {@Autowiredprivate ApplicationContext context;public F4 getF4() {return context.getBean(F4.class);}
}
@Scope("prototype")
@Component
public class F4 {
}

3.6 总结

        虽然上述四种方法各不相同,但理念基本相同,都是延迟其他scope bean的获取。他们都是不直接获取多例,而是在中间加一个对象,通过这个对象等到运行时再去获取多例bean 

        更推荐使用BeanFactory 或 ApplicationContext,因为更加简洁,且不像代理一样会造成一定的性能损耗。

相关文章:

Spring学习(四):Scope的介绍及其失效解决方案

目录 一、spring当中有哪些scope 二、scope初始化与销毁演示 2.1 scope的初始化 2.2 scope的销毁 三、scope失效及其解决方案 3.1 scope失效演示 3.2 scope失效解决方案一&#xff1a;Lazy 3.3 scope失效解决方案二&#xff1a;设置proxyMode属性 3.4 scope失效解决…...

【学习集合--Set】

学习内容&#xff1a; Set集合概述Set集合实现—HashSet LinkedHashSet和TreeSet 学习产出&#xff1a; Set集合概述 Set中不存在值相同的节点。将两个对象e1.equals(e2),如果结果为true&#xff0c;或者&#xff08;e1e2&#xff09;内存地址相等&#xff0c;就认为两个对象…...

函数的参数

函数的默认实参 函数默认参数&#xff1a;函数的形参可以有默认值&#xff0c;如果我们自己传入参数&#xff0c;就用自己的数据&#xff0c;如果没有&#xff0c;那么用默认值 特别注意*&#xff1a; 如果某个位置有了默认参数&#xff0c;那么从这个位置往后&#xff0c;必…...

数组(八)-- LC[53][152] 最大子数组之和与乘积最大子数组

1 最大子数组之和 1.1 题目描述 题目链接&#xff1a;https://leetcode.cn/problems/maximum-subarray/ 1.2 求解思路 1. 暴力法 class Solution:def maxSubArray(self, nums: List[int]) -> int:length len(nums)max_sum float(-inf)for i in range(length):sum_sub_…...

docker2-zabbix

安装最新版docker yum remove docker docker-common docker-selinux docker-engine yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo yum makecache fast yum list docker-ce --showduplicates | sort -r yum install docke…...

ctcdecode安装

1.下载 https://pan.baidu.com/s/1sZMbzzYtBoT35zHtDifVqQ &#xff0c;提取码&#xff1a;a05y。然后解压到ctcdecode文件夹中。 感谢 ctcdecode安装_huangneng0219的博客-CSDN博客 提供。 然后build.py文件中的compile_args [-O3, -DKENLM_MAX_ORDER6, -stdc11, -fPIC] …...

虚树学习小记

虚树是什么 虚树指在原树上选择需要的点和它们的LCALCALCA组成的一棵树。这样可以使在树DP时顶点数更少&#xff0c;从而减少时间复杂度。一般用于有多组数据且能保证所有数据访问的点的和不超过规定范围。 情景代入&#xff1a;SDOI2011消耗战 SDOI2011消耗战 题目大意 给…...

【C++】特殊类设计(单例模式)

文章目录一、设计模式概念二、设计一个不能被拷贝的类三、设计一个只能在堆上创建对象的类3.1 私有构造3.2 私有析构四、设计一个只能在栈上创建对象的类五、设计不能被继承的类六、单例模式❗️❗️6.1 饿汉模式6.2 懒汉模式6.2.1 线程安全问题6.2.2 新写法一、设计模式概念 …...

基于YOLOv5的水下海洋目标检测

摘要&#xff1a;水下海洋目标检测技术具有广泛的应用前景&#xff0c;可以用于海洋环境监测、海洋资源开发、海洋生物学研究等领域。本文提出了一种基于 YOLOv5 的水下海洋目标检测方法&#xff0c;使用数据增强方法进行了大量实验&#xff0c;并与其他方法进行了对比&#xf…...

磁盘这列(Raid)

RAID介绍 RAID技术通过把多个硬盘设备组合成一个容量更大的、安全性更好的磁盘阵列。把数据切割成许多区段后分别放在不同的物理磁盘上&#xff0c;然后利用分散读写技术来提升磁盘阵列整体的性能&#xff0c;同时把多个重要数据的副本同步到不同的物理设备上&#xff0c;从而…...

Oracle之PL/SQL存储过程与函数练习题(七)

1.创建一个存储过程&#xff0c;以员工号为参数&#xff0c;输出该员工的工资2.创建一个存储过程&#xff0c;以员工号为参数&#xff0c;修改该员工的工资。若该员工属于10号部门&#xff0c;则工资增加150&#xff1b;若属于20号部门&#xff0c;则工资增加200&#xff1b;若…...

C++入门教程||C++ 基本的输入输出||C++ 数据结构

C 基本的输入输出 C 基本的输入输出 C 标准库提供了一组丰富的输入/输出功能&#xff0c;我们将在后续的章节进行介绍。本章将讨论 C 编程中最基本和最常见的 I/O 操作。 C 的 I/O 发生在流中&#xff0c;流是字节序列。如果字节流是从设备&#xff08;如键盘、磁盘驱动器、…...

线性表——顺序表

文章目录一&#xff1a;线性表二&#xff1a;顺序表1&#xff1a;概念与结构1&#xff1a;静态顺序表2&#xff1a;动态顺序表2&#xff1a;动态顺序表的代码实现1&#xff1a;结构2&#xff1a;接口实现1&#xff1a;初始化2&#xff1a;释放内存3&#xff1a;检查容量4&#…...

第六章 Vite4+Vue3+Vtkjs 模型颜色切换、漫反射曲面颜色

一、介绍 💥 💥 Vtk里面工具非常的齐全,但是相关的文档又少之又少,只能花大量时间去阅读源码。漫反射曲面颜色是什么意思呢,Vtk可以使用漫反射曲面颜色来模拟光线在表面反射时的颜色。漫反射是一种光线与表面发生碰撞后,被散射到各个方向的现象,这种现象可以用来解释物…...

【QT学习七】QTreeWidget

目录 一、QTreeWidget 概述 二、QTreeWidget 的基本使用 2.1、创建 QTreeWidget 控件 2.2、设置 QTreeWidget 的大小和位置 2.3、设置 QTreeWidget 的列数和列标题 2.4、添加节点 2.5、读取节点 2.6、设置节点数据 2.7、自定义节点样式 三、注意事项 四、完整示例 一…...

【Linux】组管理和权限管理

目录1 Linux组的基本介绍2 文件/目录所有者2.1 查看文件的所有者2.2 修改文件所有者3 组的创建3.1 基本指令3.2 应用实例4 文件/目录 所在组4.1 查看文件/目录所在组4.2修改文件/目录所在的组5 其他组6 改变用户所在组6.1 改变用户所在的组6.2 应用实例7 权限介绍8 rwx权限详解…...

从零到一发布 NPM 包

如果你负责前端的基础能力建设&#xff0c;发布各种功能/插件包犹如家常便饭&#xff0c;所以熟悉对 npm 包的发布与管理是非常有必要的&#xff0c;故此有了本篇总结文章。本篇文章一方面总结&#xff0c;一方面向社区贡献开箱即用的 npm 开发、编译、发布、调试模板&#xff…...

uniapp国际化配置

1、创建资源文件 创建一个locale文件夹&#xff0c;新增index.js,en.json,zh-hans.json 2.配置locale文件夹中的index.js文件 import Vue from vue import VueI18n from vue-i18n// v8.x import en from ./en.json import zhHans from ./zh-Hans.json import zhHant from .…...

前端中 try-catch 捕获不到哪些异常和常见错误

在开发过程中&#xff0c;我们的目标是 0error&#xff0c;0warning。 但有很多因素并不是我们可控的&#xff0c;为了避免某块代码的错误&#xff0c;影响到其他模块或者整体代码的运行&#xff0c;我们经常会使用try-catch模块来主动捕获一些异常或者错误。 比如我们在获取…...

javaEE 初阶 — 如何构造一个 HTTP 请求

文章目录使用 form 表单标签构造1 构造 GET 请求2 构造 POST 请求使用 ajax 构造1 什么是异步2 代码中如何使用 ajax使用第三方工具构造1 postman 工具的安装2 postman 工具的使用使用 form 表单标签构造 1 构造 GET 请求 使用 form 表单构造 HTTP 请求&#xff0c;需要用到两…...

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周&#xff0c;有很多同学在写期末Java web作业时&#xff0c;运行tomcat出现乱码问题&#xff0c;经过多次解决与研究&#xff0c;我做了如下整理&#xff1a; 原因&#xff1a; IDEA本身编码与tomcat的编码与Windows编码不同导致&#xff0c;Windows 系统控制台…...

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...

使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装

以下是基于 vant-ui&#xff08;适配 Vue2 版本 &#xff09;实现截图中照片上传预览、删除功能&#xff0c;并封装成可复用组件的完整代码&#xff0c;包含样式和逻辑实现&#xff0c;可直接在 Vue2 项目中使用&#xff1a; 1. 封装的图片上传组件 ImageUploader.vue <te…...

【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)

骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术&#xff0c;它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton)&#xff1a;由层级结构的骨头组成&#xff0c;类似于人体骨骼蒙皮 (Mesh Skinning)&#xff1a;将模型网格顶点绑定到骨骼上&#xff0c;使骨骼移动…...

GruntJS-前端自动化任务运行器从入门到实战

Grunt 完全指南&#xff1a;从入门到实战 一、Grunt 是什么&#xff1f; Grunt是一个基于 Node.js 的前端自动化任务运行器&#xff0c;主要用于自动化执行项目开发中重复性高的任务&#xff0c;例如文件压缩、代码编译、语法检查、单元测试、文件合并等。通过配置简洁的任务…...

力扣热题100 k个一组反转链表题解

题目: 代码: func reverseKGroup(head *ListNode, k int) *ListNode {cur : headfor i : 0; i < k; i {if cur nil {return head}cur cur.Next}newHead : reverse(head, cur)head.Next reverseKGroup(cur, k)return newHead }func reverse(start, end *ListNode) *ListN…...

GitHub 趋势日报 (2025年06月06日)

&#x1f4ca; 由 TrendForge 系统生成 | &#x1f310; https://trendforge.devlive.org/ &#x1f310; 本日报中的项目描述已自动翻译为中文 &#x1f4c8; 今日获星趋势图 今日获星趋势图 590 cognee 551 onlook 399 project-based-learning 348 build-your-own-x 320 ne…...

Java求职者面试指南:Spring、Spring Boot、Spring MVC与MyBatis技术解析

Java求职者面试指南&#xff1a;Spring、Spring Boot、Spring MVC与MyBatis技术解析 一、第一轮基础概念问题 1. Spring框架的核心容器是什么&#xff1f;它的作用是什么&#xff1f; Spring框架的核心容器是IoC&#xff08;控制反转&#xff09;容器。它的主要作用是管理对…...

从零开始了解数据采集(二十八)——制造业数字孪生

近年来&#xff0c;我国的工业领域正经历一场前所未有的数字化变革&#xff0c;从“双碳目标”到工业互联网平台的推广&#xff0c;国家政策和市场需求共同推动了制造业的升级。在这场变革中&#xff0c;数字孪生技术成为备受关注的关键工具&#xff0c;它不仅让企业“看见”设…...

pgsql:还原数据库后出现重复序列导致“more than one owned sequence found“报错问题的解决

问题&#xff1a; pgsql数据库通过备份数据库文件进行还原时&#xff0c;如果表中有自增序列&#xff0c;还原后可能会出现重复的序列&#xff0c;此时若向表中插入新行时会出现“more than one owned sequence found”的报错提示。 点击菜单“其它”-》“序列”&#xff0c;…...