当前位置:首页 > 科技  > 软件

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

来源: 责编: 时间:2024-01-08 09:17:41 336观看
导读环境:Spring5.3.23一. 简介Spring Scope Bean是Spring用于管理Bean的作用域的一种机制。它定义了容器中Bean的生命周期和实例化策略,即如何创建Bean实例。在Spring中,Bean的作用域包括单例(singleton)、原型(prototype)、请

环境:Spring5.3.23fjY28资讯网——每日最新资讯28at.com

一. 简介

Spring Scope Bean是Spring用于管理Bean的作用域的一种机制。它定义了容器中Bean的生命周期和实例化策略,即如何创建Bean实例。fjY28资讯网——每日最新资讯28at.com

在Spring中,Bean的作用域包括单例(singleton)、原型(prototype)、请求(request)、会话(session)等。每个作用域都有其特定的使用场景和行为:fjY28资讯网——每日最新资讯28at.com

  1. 单例(singleton):这是Spring默认的作用域,表示在整个Spring容器中,只有一个Bean实例存在。无论你从哪个地方获取这个Bean,都将返回同一个实例。
  2. 原型(prototype):每次从容器中请求Bean时,都会创建一个新的Bean实例。
  3. 请求(request):在一个HTTP请求的范围内,Bean是单例的。这种作用域适用于与单个请求关联的Bean。
  4. 会话(session):在一个HTTP会话的范围内,Bean是单例的。这种作用域适用于与单个用户会话关联的Bean。

此外,Spring还提供了其他一些作用域应用(Application)、WebSocket,以满足不同场景的需求。fjY28资讯网——每日最新资讯28at.com

通过合理地选择Bean的作用域,可以优化应用的性能和资源利用率。例如,对于需要频繁创建和销毁实例的Bean,使用原型作用域会更高效;而对于需要在多个请求或会话之间共享状态的Bean,则可以选择单例或会话作用域。附官方图:fjY28资讯网——每日最新资讯28at.com

图片图片fjY28资讯网——每日最新资讯28at.com

接下来将分别介绍每一种作用域bean。fjY28资讯网——每日最新资讯28at.com

二. 作用域应用

基础类

static class Person {  @Override  public String toString() {    return super.toString() + " - " + this.hashCode() + "" ;  }}

2.1 单例(singleton)

默认使用@Bean,@Service,@Controller注解标注的注解都是单例的。也可以同@Scope注解指定作用域为单例fjY28资讯网——每日最新资讯28at.com

@Bean// 不指定@Scope默认就是单例@Scope(value = "singleton")public Person person() {  return new Person() ;}

测试fjY28资讯网——每日最新资讯28at.com

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)) ;}

控制台输出fjY28资讯网——每日最新资讯28at.com

com.pack.main.scope.ScopeMain5$Person@5e0e82ae - 1578009262com.pack.main.scope.ScopeMain5$Person@5e0e82ae - 1578009262

每次获取的都是同一个实例。fjY28资讯网——每日最新资讯28at.com

原理fjY28资讯网——每日最新资讯28at.com

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实例。fjY28资讯网——每日最新资讯28at.com

@Bean@Scope(value = "prototype")public Person person() {  return new Person() ;}

控制台输出fjY28资讯网——每日最新资讯28at.com

com.pack.main.scope.ScopeMain5$Person@fa4c865 - 262457445com.pack.main.scope.ScopeMain5$Person@3bd82cf5 - 1004023029

每次获取都是不同的对象。fjY28资讯网——每日最新资讯28at.com

原理fjY28资讯网——每日最新资讯28at.com

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?fjY28资讯网——每日最新资讯28at.com

2.3 请求(request)

接下来都是与web环境相关了,所以这里演示的示例会以SpringBoot3.0.5环境演示。fjY28资讯网——每日最新资讯28at.com

基础类fjY28资讯网——每日最新资讯28at.com

@Component@Scope(value = "request")public class Person {}

测试类fjY28资讯网——每日最新资讯28at.com

@RestController@RequestMapping("/scopes")public class ScopeController {  @Resource  private Person person ;  @Resource  private PersonService ps ;  @GetMapping("/request")  public Person request() {    System.out.println("ScopeController: " + person) ;    ps.query() ;    return person ;  }}

ServicefjY28资讯网——每日最新资讯28at.com

@Servicepublic class PersonService {  @Resource  private Person person ;  public void query() {    System.out.println("PersonService: " + person) ;  }}

如果上面这样配置,启动服务将会报错:fjY28资讯网——每日最新资讯28at.com

Caused by: java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.  at org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131) ~[spring-web-6.0.7.jar:6.0.7]

该错误的原因就是你在一个单例bean中注入一个request作用域的bean,而request作用域bean的生命周期是在一个web请求开始创建的,所以这里你当然是没法注入的。fjY28资讯网——每日最新资讯28at.com

解决办法:fjY28资讯网——每日最新资讯28at.com

  • @Scope设置代理模式
@Component@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)public class Person {}

测试结果fjY28资讯网——每日最新资讯28at.com

ScopeController: com.pack.scopes.Person@106a9684 - 275420804PersonService: com.pack.scopes.Person@106a9684 - 275420804ScopeController: com.pack.scopes.Person@64396678 - 1681483384PersonService: com.pack.scopes.Person@64396678 - 1681483384

每次请求接口都获取的不是同一个实例。并且在一个完整的请求中获取的Person都是同一个。fjY28资讯网——每日最新资讯28at.com

  • 使用@RequestScope

该注解原理与上面其实一致的fjY28资讯网——每日最新资讯28at.com

@Scope(WebApplicationContext.SCOPE_REQUEST)public @interface RequestScope {  @AliasFor(annotation = Scope.class)  // 设置好了使用代理  ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;}

2.4 会话(session)

@Component@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)// 与request一样,必须设置代理模式或者使用下面这个注解// @SessionScopepublic class Person {}

测试fjY28资讯网——每日最新资讯28at.com

ScopeController: com.pack.scopes.Person@2b56038d - 727057293PersonService: com.pack.scopes.Person@2b56038d - 727057293ScopeController: com.pack.scopes.Person@2b56038d - 727057293PersonService: com.pack.scopes.Person@2b56038d - 727057293

多次访问都是同一个session;你再换个浏览器访问fjY28资讯网——每日最新资讯28at.com

ScopeController: com.pack.scopes.Person@1aa201fd - 446824957PersonService: com.pack.scopes.Person@1aa201fd - 446824957ScopeController: com.pack.scopes.Person@1aa201fd - 446824957PersonService: com.pack.scopes.Person@1aa201fd - 446824957

此时对象就是一个新的了,不同的浏览器访问当然不是同一个session了。fjY28资讯网——每日最新资讯28at.com

2.5 应用(application)

@Scope(value = "application", proxyMode = ScopedProxyMode.TARGET_CLASS)// @ApplicationScope// 都是web环境,所以情况都一样public class Person {}

测试fjY28资讯网——每日最新资讯28at.com

360浏览器fjY28资讯网——每日最新资讯28at.com

ScopeController: com.pack.scopes.Person@6371b4b6 - 1668396214PersonService: com.pack.scopes.Person@6371b4b6 - 1668396214

Chrome浏览器fjY28资讯网——每日最新资讯28at.com

ScopeController: com.pack.scopes.Person@6371b4b6 - 1668396214PersonService: com.pack.scopes.Person@6371b4b6 - 1668396214

他们是同一个对象,application作用域生命周期与整个应用一样,只有你关闭了服务器,在启动后才会是再重新创建的bean对象。fjY28资讯网——每日最新资讯28at.com

3. web作用域原理

3.1 注册作用域

public abstract class AbstractApplicationContext {  public void refresh() {    postProcessBeanFactory(beanFactory);  }}public class AnnotationConfigServletWebServerApplicationContext {  protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {    super.postProcessBeanFactory(beanFactory);  }}public class ServletWebServerApplicationContext {  protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {    // ...    registerWebApplicationScopes();  }  private void registerWebApplicationScopes() {    WebApplicationContextUtils.registerWebApplicationScopes(getBeanFactory());  }}public abstract class WebApplicationContextUtils {  public static void registerWebApplicationScopes(ConfigurableListableBeanFactory beanFactory) {    registerWebApplicationScopes(beanFactory, null);  }  public static void registerWebApplicationScopes(ConfigurableListableBeanFactory beanFactory,      @Nullable ServletContext sc) {    // 注册作用域    beanFactory.registerScope(WebApplicationContext.SCOPE_REQUEST, new RequestScope());    beanFactory.registerScope(WebApplicationContext.SCOPE_SESSION, new SessionScope());    if (sc != null) {      ServletContextScope appScope = new ServletContextScope(sc);      beanFactory.registerScope(WebApplicationContext.SCOPE_APPLICATION, appScope);    }  }}

这里每一种web作用域都有一个对应的Scope实现RequestScope,SessionScope,ServletContextScope。fjY28资讯网——每日最新资讯28at.com

3.2 查找web作用域beanfjY28资讯网——每日最新资讯28at.com

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,如上面的web作用域    else {      String scopeName = mbd.getScope();      Scope scope = this.scopes.get(scopeName);      if (scope == null) {        throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");      }      try {          // 通过具体Scope的实现类获取bean对象        Object scopedInstance = scope.get(beanName, () -> {          beforePrototypeCreation(beanName);          try {            // 首次都还是会创建            return createBean(beanName, mbd, args);            }          });        }      }    }    // ...  }}

总结:Spring Scope Bean是Spring框架中用于管理Bean的作用域的机制,它定义了Bean的生命周期和实例化策略。通过合理地选择Bean的作用域,可以优化应用的性能和资源利用率。fjY28资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-57916-0.html玩转Spring各种作用域Bean Scope及源码分析

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: MongoDB 大量数据插入时的性能影响及解决方法

下一篇: 2023年最火的前端项目出炉,竟然是它?

标签:
  • 热门焦点
  • 影音体验是真的强 简单聊聊iQOO Pad

    大公司的好处就是产品线丰富,非常细分化的东西也能给你做出来,例如早先我们看到了新的vivo Pad2,之后我们又在iQOO Neo8 Pro的发布会上看到了iQOO的首款平板产品iQOO Pad。虽
  • 28个SpringBoot项目中常用注解,日常开发、求职面试不再懵圈

    前言在使用SpringBoot开发中或者在求职面试中都会使用到很多注解或者问到注解相关的知识。本文主要对一些常用的注解进行了总结,同时也会举出具体例子,供大家学习和参考。注解
  • 只需五步,使用start.spring.io快速入门Spring编程

    步骤1打开https://start.spring.io/,按照屏幕截图中的内容创建项目,添加 Spring Web 依赖项,并单击“生成”按钮下载 .zip 文件,为下一步做准备。请在进入步骤2之前进行解压。图
  • Temu起诉SHEIN,跨境电商战事升级

    来源 | 伯虎财经(bohuFN)作者 | 陈平安日前据外媒报道,拼多多旗下跨境电商平台Temu正对竞争对手SHEIN提起新诉讼,诉状称Shein&ldquo;利用市场支配力量强迫服装厂商与之签订独家
  • 10天营收超1亿美元,《星铁》比《原神》差在哪?

    来源:伯虎财经作者:陈平安即便你没玩过《原神》,你一定听说过的它的大名。恨它的人把《原神》开服那天称作是中国游戏史上最黑暗的一天,有粉丝因为索尼在PS平台上线《原神》,怒而
  • ESG的面子与里子

    来源 | 光子星球撰文 | 吴坤谚编辑 | 吴先之三伏大幕拉起,各地高温预警不绝,但处于厄尔尼诺大&ldquo;烤&rdquo;之下的除了众生,还有各大企业发布的ESG报告。ESG是&ldquo;环境保
  • Android 14发布:首批适配机型公布

    5月11日消息,谷歌在今天凌晨举行了I/O大会,本次发布会谷歌带来了自家的AI语言模型PaLM 2、谷歌Pixel Fold折叠屏、谷歌Pixel 7a手机,同时发布了Androi
  • 苹果140W USB-C充电器:采用氮化镓技术

    据10 月 30 日 9to5 Mac 消息报道,当苹果推出新的 MacBook Pro 2021 时,该公司还推出了新的 140W USB-C 充电器,附赠在 MacBook Pro 16 英寸机型的盒子里,也支
  • 华为举行春季智慧办公新品发布会 首次推出电子墨水屏平板

    北京时间2月27日晚,华为在巴塞罗那举行春季智慧办公新品发布会,在海外市场推出之前已经在中国市场上市的笔记本、平板、激光打印机等办公产品,并首次推出搭载
Top