阅读之前要注意的东西:本文就是主打流水账式的源码阅读,主导的是一个参考,主要内容需要看官自己去源码中验证。全系列文章基于 spring 源码 5.x 版本。

写在开始前的话:

阅读spring 源码实在是一件庞大的工作,不说全部内容,单就最基本核心部分包含的东西就需要很长时间去消化了:

  • beans
  • core
  • context

实际上我在博客里贴出来的还只是一部分内容,更多的内容,我放在了个人,fork自 spring 官方源码仓了; 而且对源码的学习,必须是要跟着实际代码层层递进的,不然只是干巴巴的文字味同嚼蜡。

https://gitee.com/bokerr/spring-framework-5.0.x-study

这个仓设置的公共仓,可以直接拉取。

Spring源码阅读系列--全局目录.md

作用域 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 有哪些的更多相关文章

  1. Bean的作用域scope

    Bean的作用域scope 1.singleton 单例,指一个bean容器中只存在一份 2.prototype 每次请求(每次使用)创建新的实例,destroy方式不生效 3.request 每次h ...

  2. spring之bean的作用域scope的值的详解

    今天研究了一下scope的作用域.默认是单例模式,即 scope="singleton".另外scope还有prototype.request.session.global ses ...

  3. Spring系列四:Bean Scopes作用域

    等闲识得东风面,万紫千红总是春. 概述 在Spring框架中,我们可以在六个内置的spring bean作用域中创建bean,还可以定义bean范围.在这六个范围中,只有在使用支持Web的applic ...

  4. Spring系列8:bean的作用域

    本文内容 bean定义信息的意义 介绍6种bean的作用域 bean定义信息的意义 Spring中区分下类.类定义信息,类实例对象的概念?不容易理解,以餐馆中点炒饭为例. 类: 相当于你看到菜单上炒饭 ...

  5. Spring学习记录(五)---bean的作用域scope

    作用域:singleton:单例,整个应用中只创建一个实例(默认) prototype:原型,每次注入时都新建一个实例 session:会话,每个会话创建一个实例 request:请求,每个请求创建一 ...

  6. (三)Spring 高级装配 bean的作用域@Scope

    1.默认情况下,spring通过@Autowared注入的bean是单例的bean,但有些情况是不满足的,例如:购物车,每个会话,或每个用户登录使用的购物车都是独立的 spring的定义的作用域: a ...

  7. Spring中bean的作用域scope详解

    参考文献:http://blog.csdn.net/jacklearntech/article/details/40157861 http://www.cnblogs.com/qq78292959/p ...

  8. Spring初学之bean之间的关系和bean的作用域

    一.bean之间的关系 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="h ...

  9. Spring源码系列(二)--bean组件的源码分析

    简介 spring-bean 组件是 Spring IoC 的核心,我们可以使用它的 beanFactory 来获取所需的对象,对象的实例化.属性装配和初始化等都可以交给 spring 来管理. 本文 ...

  10. 【Spring】IoC容器 - Spring Bean作用域Scope(含SpringCloud中的RefreshScope )

    前言 上一章学习了[依赖来源],本章主要讨论SpringBean的作用域,我们这里讨论的Bean的作用域,很大程度都是默认只讨论依赖来源为[Spring BeanDefinition]的作用域,因为在 ...

随机推荐

  1. [转帖]kubernetes(k8s):容器资源限制(内存限制、cpu限制、namespace限制)

    文章目录 1. k8s容器资源限制 2. 内存资源限制实例 3. cpu资源限制 4. namespace设置资源限制 5. namespace中pod的配额 6. namespace的创建.使用和删 ...

  2. [转帖]SQL Server中查询CPU占用高的SQL语句

    本文导读:触发器造成死锁.作业多且频繁.中间表的大量使用.游标的大量使用.索引的设计不合理.事务操作频繁.SQL语句设计不合理,都会造成查询效率低下.影响服务器性能的发挥.我们可以使用sql serv ...

  3. [转帖]prometheus node-exporter 全部指标说明

    https://www.cnblogs.com/276815076/p/16383615.html Basic CPU / Mem / Disk Info Basic CPU / Mem / Disk ...

  4. [转帖]Oracle 创建和查看DBLink 的方法

    https://www.cnblogs.com/zhouzangood/articles/4612441.html 1.如果需要创建全局 DBLink,则需要先确定用户有创建 dblink 的权限: ...

  5. Python学习之四_部分battery的学习

    Python学习之四_部分battery的学习 CentOS升级pip的方法 yum install python3 之后, 一般centos的版本都很低, 有时候很多module 是无法安装的. 一 ...

  6. [转帖]Skywalking学习及整合springboot

    目录 1. Skywalking概述 2. Skywalking主要功能 3. Skywalking主要特性 4. Skywalking架构简介 5. Spring Cloud与Skywalking实 ...

  7. [转帖]计算机体系结构-重排序缓存ROB

    https://zhuanlan.zhihu.com/p/501631371 在现代处理器中,重排序缓存(Reorder Buffer,即ROB)是一个至关重要的概念,一个标准的乱序执行处理器在其多个 ...

  8. redis-shake

    https://github.com/alibaba/RedisShake/wiki/%E8%BF%90%E8%A1%8C%E7%9B%91%E6%8E%A7 redis-shake is a too ...

  9. js中toString方法的三个作用

    toString方法的三个作用: 1.返回一个[表示对象]的[字符串] 2.检测对象的类型 Object.prototype.toString.call(arr)==="[object Ar ...

  10. Seata分布式事务 (理论与部署相结合)

    分布式事务--Seata 分布式事务 1. 本地事务与分布式事务 1.1 本地事务 本地事务,也就是传统的单机事务.在传统数据库事务中,必须要满足四个原则: 1.2 分布式事务问题 分布式事务,就是指 ...