成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

玩轉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,則可以選擇單例或會話作用域。附官方圖:

圖片圖片

接下來將分別介紹每一種作用域bean。

二. 作用域應用

基礎類

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

2.1 單例(singleton)

默認使用@Bean,@Service,@Controller注解標注的注解都是單例的。也可以同@Scope注解指定作用域為單例

@Bean
// 不指定@Scope默認就是單例
@Scope(value = "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 - 1578009262
com.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 = "prototype")
public Person person() {
  return new Person() ;
}

控制臺輸出

com.pack.main.scope.ScopeMain5$Person@fa4c865 - 262457445
com.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 = "request")
public class Person {
}

測試類

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

Service

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

如果上面這樣配置,啟動服務將會報錯:

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請求開始創建的,所以這里你當然是沒法注入的。

解決辦法:

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

測試結果

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

每次請求接口都獲取的不是同一個實例。并且在一個完整的請求中獲取的Person都是同一個。

  • 使用@RequestScope

該注解原理與上面其實一致的

@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一樣,必須設置代理模式或者使用下面這個注解
// @SessionScope
public class Person {}

測試

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

多次訪問都是同一個session;你再換個瀏覽器訪問

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

此時對象就是一個新的了,不同的瀏覽器訪問當然不是同一個session了。

2.5 應用(application)

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

測試

360瀏覽器

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

Chrome瀏覽器

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

他們是同一個對象,application作用域生命周期與整個應用一樣,只有你關閉了服務器,在啟動后才會是再重新創建的bean對象。

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。

3.2 查找web作用域bean

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的作用域,可以優化應用的性能和資源利用率。

責任編輯:武曉燕 來源: Spring全家桶實戰案例源碼
相關推薦

2021-07-05 08:43:46

Spring Beanscope作用域

2023-09-05 08:23:56

SpringScope方法

2011-03-18 09:27:00

Spring

2022-05-27 08:25:55

容器Spring

2024-01-29 08:28:01

Spring事務失效

2024-11-26 17:43:51

2024-11-14 14:53:04

2022-11-29 17:38:57

DockerfileARG作用域

2021-04-28 06:26:11

Spring Secu功能實現源碼分析

2023-06-29 08:32:41

Bean作用域

2010-08-25 15:19:20

DHCP作用域

2016-12-19 11:10:32

JavaScript變量作用域

2023-10-07 09:16:55

SpringBoot啟動流程

2022-03-07 10:05:02

SpringStreamMQ連接

2021-06-02 07:02:42

js作用域函數

2022-08-31 07:04:50

Bean作用域

2021-08-09 11:15:28

MybatisJavaSpring

2012-02-23 12:53:40

JavaPlay Framew

2011-09-06 09:56:24

JavaScript

2010-07-08 13:23:23

UML面向對象
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 日韩精品成人 | 91久久精品国产91久久性色tv | 国产一级电影在线 | 国产精品视频网 | 日韩中文久久 | 污视频在线免费观看 | 高清视频一区二区三区 | 亚洲最大看片网站 | 国产乱码精品一区二三赶尸艳谈 | 成人中文字幕在线观看 | 91福利在线观看视频 | 午夜精品一区二区三区免费视频 | 欧美精品免费观看二区 | 99久久99 | 最新日韩在线视频 | 日韩一区二区三区在线观看 | 欧美激情一区 | 日韩成人免费视频 | 羞羞视频免费观 | 在线视频中文字幕 | 天天玩夜夜操 | 久久男人天堂 | 久久影音先锋 | 亚洲一区精品在线 | 正在播放国产精品 | 免费激情网站 | 欧美片网站免费 | 成年人在线视频 | 日韩欧美一区二区三区四区 | 亚洲一级av毛片 | 精品一区二区久久久久久久网站 | 九九热在线观看 | 黄网站免费在线观看 | 亚洲不卡在线观看 | 日韩av免费看 | 欧美日韩一区在线观看 | 亚洲国产精品99久久久久久久久 | 在线播放国产一区二区三区 | 日韩av大片免费看 | 亚洲顶级毛片 | 国产女人第一次做爰毛片 |