阅读之前要注意的东西:本文就是主打流水账式的源码阅读,主导的是一个参考,主要内容需要看官自己去源码中验证。全系列文章基于 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. SkyWalking的学习之三

    SkyWalking的学习之三 持续优化 SkyWalking 默认可以使用h2,但是感觉容量和性能都可能不太好 所以我想使用一下elasticSearch进行替换. 自己其实一直想心想去学习, 但是 ...

  2. [转帖]@Scope("prototype")的正确用法——解决Bean的多例问题

    https://www.jianshu.com/p/54b0711a8ec8 1. 问题,Spring管理的某个Bean需要使用多例   在使用了Spring的web工程中,除非特殊情况,我们都会选择 ...

  3. [转帖]SkyWalking集成logback

    1.引入skywalking的jar包,导入的包和agent版本一致 <dependency> <groupId>org.apache.skywalking</group ...

  4. [转帖]⭐万字长篇超详细的图解Tomcat中间件方方面面储备知识⭐

    https://developer.aliyun.com/article/885079?spm=a2c6h.24874632.expert-profile.321.7c46cfe9h5DxWK 202 ...

  5. Linux应用程序下网络栈参数的简单整理

    somaxconn 该参数应该是决定一个服务能够同时处理多少个网络请求的核心参数. 一个程序能够支持多少个访问参数,是有两部分来决定, 第一部分是somaxconn ,第二部分是应用服务器启动时传递过 ...

  6. canvas实现添加水印

    canvas添加水印思路 1.在画布上写上水印的名称(时间加上用户名) 2.canvas转化为base64,作为body的背景色 3.优化倾斜度和透明度 4.如果用户去除body的style水印消失 ...

  7. vue3关于.sync的用法

    场景描述 我们都知道,子组件是不能够去修改父组件传递过来的数据. 因为如果子组件去修改父组件件传递过来的数据. 会导致数据的应用流向变得难以理解. 但是有些时候,我们需要当子组件的数据变化后,父组件的 ...

  8. vue中sync的使用原来这么简单

    sync的使用场景 有些时候子组件需要修改父组件传递过来的prop, 要去改变父组件的状态的时候就需要使用aync 看见这里有些同学可能会问?? 不是说不可以修改父组件传递到子组件的值吗? 为啥要修改 ...

  9. 【JS 逆向百例】37网游登录接口参数逆向

    声明 本文章中所有内容仅供学习交流,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除! 逆向目标 目标:37网游登录 主页:https://www.37.co ...

  10. IConfigurationSectionHandler 接口的用法

    今天终于花了点时间了解一下IConfigurationSectionHandler 接口的用法 ,引以入门.首先建立一 RobsunConfigSectionHandler 专案,代码如下 : nam ...