玩转Spring各种作用域Bean Scope及源码分析

Spring Scope Bean是Spring框架中用于管理Bean的作用域的机制,它定义了Bean的生命周期和实例化策略 。通过合理地选择Bean的作用域,可以优化应用的性能和资源利用率 。环境:Spring5.3.23
一. 简介Spring Scope Bean是Spring用于管理Bean的作用域的一种机制 。它定义了容器中Bean的生命周期和实例化策略,即如何创建Bean实例 。
在Spring中 , Bean的作用域包括单例(singleton)、原型(prototype)、请求(request)、会话(session)等 。每个作用域都有其特定的使用场景和行为:

  1. 单例(singleton):这是Spring默认的作用域,表示在整个Spring容器中,只有一个Bean实例存在 。无论你从哪个地方获取这个Bean , 都将返回同一个实例 。
  2. 原型(prototype):每次从容器中请求Bean时,都会创建一个新的Bean实例 。
  3. 请求(request):在一个HTTP请求的范围内,Bean是单例的 。这种作用域适用于与单个请求关联的Bean 。
  4. 会话(session):在一个HTTP会话的范围内,Bean是单例的 。这种作用域适用于与单个用户会话关联的Bean 。
此外,Spring还提供了其他一些作用域应用(Application)、WebSocket,以满足不同场景的需求 。
通过合理地选择Bean的作用域,可以优化应用的性能和资源利用率 。例如 , 对于需要频繁创建和销毁实例的Bean,使用原型作用域会更高效;而对于需要在多个请求或会话之间共享状态的Bean , 则可以选择单例或会话作用域 。附官方图:
玩转Spring各种作用域Bean Scope及源码分析

文章插图
图片
接下来将分别介绍每一种作用域bean 。
二. 作用域应用基础类static class Person {@Overridepublic String toString() {return super.toString() + " - " + this.hashCode() + "" ;}}2.1 单例(singleton)默认使用@Bean,@Service,@Controller注解标注的注解都是单例的 。也可以同@Scope注解指定作用域为单例
@Bean// 不指定@Scope默认就是单例@Scope(value = https://www.isolves.com/it/cxkf/jiagou/2024-01-05/"singleton")public Person person() {return new Person() ;}测试
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) {context.registerBean(Config.class) ;context.refresh() ;System.out.println(context.getBean(Person.class)) ;System.out.println(context.getBean(Person.class)) ;}控制台输出
com.pack.mAIn.scope.ScopeMain5$Person@5e0e82ae - 1578009262com.pack.main.scope.ScopeMain5$Person@5e0e82ae - 1578009262每次获取的都是同一个实例 。
原理
public abstract class AbstractBeanFactory {protected <T> T doGetBean(...) {// ...// 判断是否是单例if (mbd.isSingleton()) {// 先从单例池中查找是否已经存在 , 不存在则调用createBean创建,// 然后存入单例池中sharedInstance = getSingleton(beanName, () -> {try {return createBean(beanName, mbd, args);}});}// ...}}2.2 原型(prototype)每次从容器中请求Bean时 , 都会创建一个新的Bean实例 。
@Bean@Scope(value = https://www.isolves.com/it/cxkf/jiagou/2024-01-05/"prototype")public Person person() {return new Person() ;}控制台输出
com.pack.main.scope.ScopeMain5$Person@fa4c865 - 262457445com.pack.main.scope.ScopeMain5$Person@3bd82cf5 - 1004023029每次获取都是不同的对象 。
原理
public abstract class AbstractBeanFactory {protected <T> T doGetBean(...) {// ...// 判断是否是单例if (mbd.isSingleton()) {// ...}// 判断是否是原型else if (mbd.isPrototype()) {Object prototypeInstance = null;try {// 不存在什么缓存池,直接创建bean实例返回prototypeInstance = createBean(beanName, mbd, args);}}// ...}}这里考虑一个问题,如何在单例bean中正确的注入原型bean?
2.3 请求(request)接下来都是与web环境相关了,所以这里演示的示例会以SpringBoot3.0.5环境演示 。
基础类
@Component@Scope(value = https://www.isolves.com/it/cxkf/jiagou/2024-01-05/"request")public class Person {}测试类
@RestController@RequestMapping("/scopes")public class ScopeController {@Resourceprivate Person person ;@Resourceprivate PersonService ps ;@GetMapping("/request")public Person request() {System.out.println("ScopeController: " + person) ;ps.query() ;return person ;}}Service
@Servicepublic class PersonService {@Resourceprivate Person person ;public void query() {System.out.println("PersonService: " + person) ;}}


推荐阅读