《系列二》-- 2、bean 的作用域: Scope 有哪些
阅读之前要注意的东西:本文就是主打流水账式的源码阅读,主导的是一个参考,主要内容需要看官自己去源码中验证。全系列文章基于 spring 源码 5.x 版本。
写在开始前的话:
阅读spring 源码实在是一件庞大的工作,不说全部内容,单就最基本核心部分包含的东西就需要很长时间去消化了:
- beans
- core
- context
实际上我在博客里贴出来的还只是一部分内容,更多的内容,我放在了个人,fork自 spring 官方源码仓了; 而且对源码的学习,必须是要跟着实际代码层层递进的,不然只是干巴巴的文字味同嚼蜡。
这个仓设置的公共仓,可以直接拉取。
作用域 Scope 特性概述
Spring 容器支持以下6种 scopes
常规作用域
在常规的 spring IOC 场景下使用
- prototype: 原型模式
跟单例模式相反,每次使用beanName 向spring 容器申请bean的时候,必须重新new一个bean对象。
如下案例,向spring 容器中注入一个 "原型模式" 作用域的bean, 每次当需要注入 WorkerService 时,都会创建一个全新的bean 对象。
@Component
@Scope("prototype")
public class WorkerService {
public void handler(String arg) {
this.workerDo();
}
public void workerDo() {
// xx 业务逻辑
}
}
- singleton:单例模式<Spring默认的bean作用域>
当用户通过getBean(beanName) 向容器主动申请bean时、或者由 spring 容器自身进行依赖注入操作时;必须保证被当作依赖,注入的单例bean全局唯一。
如下案例:通过注解的方式向容器中注入一个单例bean, spring 容器在第一次创建 UserService 这个bean后会将其缓存下来。
后续通过同样的beanName 来获取这个bean的时候,Spring 容器会将之前缓存的bean引用返回,从而保证 UserService 的bean 全局唯一。
@Component
@Scope("singleton")
public class UserService {
@Autowired
private WorkerService workerService;
public void handler(String arg) {
workerService.handler(arg);
System.out.println("workerService实例对象内存地址:" + workerService);
}
}
web 场景作用域
request: 该作用域的bean,在每次HTTP 请求发生时被创建,它生命周期被,该HTTP请求的生命周期包含。(只有一个bean实例)
session: session作用域的bean,在session的生命周期内有效。(只有一个bean实例)
application: 该作用域的bean 在 ServletContext 的生命周期内有效。(只有一个bean实例)
webSocket: 在webSocket生命周期内有效。(只有一个bean实例)
实际上当下 Spring Web MVC 已经显现颓势,大势已经转向了前后端分离,所以 Web 场景的 bean 作用域的使用场景已经越来越少,本文也不再详细展开。
经典问题
单例作用域的 UserService 中注入,原型作用域的 WorkerService;必须保证从 spring 容器中多次获取 UserService 时,其中注入的 WorkerService 每次都必须不一样。
模拟场景
结合上边讲解单例和原型作用域时,给的伪代码,已知:
UserService 是单例作用域
- WorkerService 将作为依赖注入到 UserService 中
WorkerService 是原型作用域
看如下的程序1和程序2,如果我们从不同的地方,多次使用 UserService.handler(arg), 你猜猜打印出来的 WorkerService 对象地址会是怎样的?
程序1:
@Controller
public class BusinessOld {
@Autowired
private UserService userService;
public void execute(String arg) {
userService.handler("arg");
}
}
程序2:
@Controller
public class BusinessNew {
@Autowired
private UserService userService;
public void execute(String arg) {
userService.handler("arg");
}
}
实际上运行上述的两段代码,输出的 workerService 对象内存地址将会一样. 前边也讲过了spring 对单例bean的管理模式:
单例bean 在第一次被加载后,会被直接缓存在容器中,后续再向容器请求这个单例 bean时,会将之前加载好的bean 直接返回
再说直白点,在UserService 类的单例bean 在第一次被初始化时,其上注入的 WorkerService 类对象就已经注入成功了,由于在UserService 是通过单例的形式注入容器的。
所以它只会被初始化一次,所以 WorkerService 也只会被注入 UserService 中一次。自然就会打印一模一样的 WorkerService 地址了
所以,上述的 程序1 和 程序2 将会输出同样的 WorkerService 对象地址。
解决办法
方法一
借助前边讲过的 "lookup-method"实现

简单回归下这个标签的作用,lookup-method.name 属性配置一个方法名, lookup-method.bean 配置一个beanId;
当从容器中加载 id="lookUpTest" 的bean 时,就会通过 lookup-method.name 配置的方法,动态的返回一个 userBean。
这里我们就不用xml的方式书写案例了,基于上述的原理,我们可以简单改一下 UserService 类的代码:
这里在 UserService.handler() 方法上加了一个注解:@Lookup 这代表着,UserService.handler() 方法每次执行时都会重新向容器获取 WorkerService 类的bean。
因为其以原型模式作用域注入容器,故此可以保证每次都能获取到一个新new出来的 WorkerService 类对象。
@Component
@Scope("singleton")
public class UserService {
@Autowired
private WorkerService workerService;
@Lookup("workerService")
public void handler(String arg) {
workerService.handler(arg);
System.out.println("workerService实例对象内存地址:" + workerService);
}
}
方法二 实现接口 BeanFactoryAware
我们首先看看这个接口的结构:
- 很简单,这里只有一个简单的Setter 方法,从这里可以接收到的参数是 BeanFactory 对象,也就是spring容器工厂本身,意思就是说我把整个车床斗给你了,你还车不出来一个珠子?

既然都拿到工厂了,你要什么bean 还不是随你喜欢,还要啥自行车。下边直接看修改好的代码:
@Component
@Scope("singleton")
public class UserService implements BeanFactoryAware {
private BeanFactory beanFactory; // 接收Setter 注入的容器对象工厂
@Override
public void setBeanFactory(Beanfactory beanFactory) {
this.beanFactory = beanFactory;
}
// @Autowired
// private WorkerService workerService;
@Lookup("workerService")
public void handler(String arg) {
// workerService.handler(arg);
WorkerService workerService = beanFactory.getBean("workerService");
workerService.handler(arg);
System.out.println("workerService实例对象内存地址:" + workerService);
}
}
这里相当于我们会在运行时动态从 BeanFactory 中获取bean: "workerService"
由于我们在bean 定义时把它的作用域定义为了 "原型模式", 故此这里当然可以从容器中捞出单例的 WorkerService
《系列二》-- 2、bean 的作用域: Scope 有哪些的更多相关文章
- Bean的作用域scope
Bean的作用域scope 1.singleton 单例,指一个bean容器中只存在一份 2.prototype 每次请求(每次使用)创建新的实例,destroy方式不生效 3.request 每次h ...
- spring之bean的作用域scope的值的详解
今天研究了一下scope的作用域.默认是单例模式,即 scope="singleton".另外scope还有prototype.request.session.global ses ...
- Spring系列四:Bean Scopes作用域
等闲识得东风面,万紫千红总是春. 概述 在Spring框架中,我们可以在六个内置的spring bean作用域中创建bean,还可以定义bean范围.在这六个范围中,只有在使用支持Web的applic ...
- Spring系列8:bean的作用域
本文内容 bean定义信息的意义 介绍6种bean的作用域 bean定义信息的意义 Spring中区分下类.类定义信息,类实例对象的概念?不容易理解,以餐馆中点炒饭为例. 类: 相当于你看到菜单上炒饭 ...
- Spring学习记录(五)---bean的作用域scope
作用域:singleton:单例,整个应用中只创建一个实例(默认) prototype:原型,每次注入时都新建一个实例 session:会话,每个会话创建一个实例 request:请求,每个请求创建一 ...
- (三)Spring 高级装配 bean的作用域@Scope
1.默认情况下,spring通过@Autowared注入的bean是单例的bean,但有些情况是不满足的,例如:购物车,每个会话,或每个用户登录使用的购物车都是独立的 spring的定义的作用域: a ...
- Spring中bean的作用域scope详解
参考文献:http://blog.csdn.net/jacklearntech/article/details/40157861 http://www.cnblogs.com/qq78292959/p ...
- Spring初学之bean之间的关系和bean的作用域
一.bean之间的关系 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="h ...
- Spring源码系列(二)--bean组件的源码分析
简介 spring-bean 组件是 Spring IoC 的核心,我们可以使用它的 beanFactory 来获取所需的对象,对象的实例化.属性装配和初始化等都可以交给 spring 来管理. 本文 ...
- 【Spring】IoC容器 - Spring Bean作用域Scope(含SpringCloud中的RefreshScope )
前言 上一章学习了[依赖来源],本章主要讨论SpringBean的作用域,我们这里讨论的Bean的作用域,很大程度都是默认只讨论依赖来源为[Spring BeanDefinition]的作用域,因为在 ...
随机推荐
- [转帖]shell 把以空格分隔的变量 分割后的每个字段赋值给变量
比如我有一个变量 "123 456 789",要求以空格为分隔符把这个变量分隔,并把分隔后的字段分别赋值给变量,即a=123:b=456:c=789 共有3中方法: 法一:先定义一 ...
- [转帖]Intel AVX 系列指令基础介绍
https://zhuanlan.zhihu.com/p/437657452 一.发展背景 1993年,Intel公司推出了奔腾处理器,该类型处理器拥有两条执行流水线,和当时的处理器相比,可以同时执行 ...
- [转帖]Redhat 8 磁盘调度策略变化:NOOP改为NONE
说明 在 redhat 4/5/6/7版本中的NOOP调度策略,从8开始修改为NONE,官方解释: none Implements a first-in first-out (FIFO) schedu ...
- [转帖]网络传输性能netperf测试方法和下载
简介 Netperf是一种网络性能的测试工具,主要针对基于TCP或UDP的传输.Netperf根据应用的不同,可以进行不同模式的网络性能测试,即批量数据传输(bulk data transfer)模式 ...
- mysql8 initialize 命令 初学版 lower_case_table_names
1. 今天开发找我跟我说 我安装的mysql 不对. 比较蛋疼. 需要修改一个参数 但是数据库已经初始进去了 重装起来比较麻烦. 硬着头皮搞. 2. 参数的名字为: lower_case_tabl ...
- click与addEventListener和removeEventListener事件与移除正确的移除事件详解
1. onclick事件 es5 普通事件就是直接触发事件,相同的事件会被覆盖掉.代码如下: let demoDiv=document.querySelector(".demo") ...
- GitHubDesktop汉化和下载
GitHubDesktop下载 这玩意官网下载速度特别慢 如果不想等待的可以戳下面链接进行下载 会实时同步更新的 下载地址:GitHubDesktop下载
- React中引入外部js文件
假设需要引入本地的一个jquery库: <script src="http://127.0.0.1:8080/plugins/jquery.min.js" type=&quo ...
- 【构建docker镜像之插件篇】使用插件实战演示:docker-maven-plugin和jib-maven-plugin
1.说明: docker容器化部署是非常流行的,Java应用如果要运行在docker环境,就要制作包含该应用的docker镜像,可以手动使用dockerfile和docker-compose制作镜像, ...
- PaddleNLP基于ERNIR3.0文本分类以中医疗搜索检索词意图分类(KUAKE-QIC)为例【多分类(单标签)】
相关项目链接: Paddlenlp之UIE模型实战实体抽取任务[打车数据.快递单] Paddlenlp之UIE分类模型[以情感倾向分析新闻分类为例]含智能标注方案) 应用实践:分类模型大集成者[Pad ...