日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當前位置:首頁 > 科技  > 軟件

玩轉Spring各種作用域Bean Scope及源碼分析

來源: 責編: 時間:2024-01-08 09:17:41 210觀看
導讀環境:Spring5.3.23一. 簡介Spring Scope Bean是Spring用于管理Bean的作用域的一種機制。它定義了容器中Bean的生命周期和實例化策略,即如何創建Bean實例。在Spring中,Bean的作用域包括單例(singleton)、原型(prototype)、請

環境:Spring5.3.23C8m28資訊網——每日最新資訊28at.com

一. 簡介

Spring Scope Bean是Spring用于管理Bean的作用域的一種機制。它定義了容器中Bean的生命周期和實例化策略,即如何創建Bean實例。C8m28資訊網——每日最新資訊28at.com

在Spring中,Bean的作用域包括單例(singleton)、原型(prototype)、請求(request)、會話(session)等。每個作用域都有其特定的使用場景和行為:C8m28資訊網——每日最新資訊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,以滿足不同場景的需求。C8m28資訊網——每日最新資訊28at.com

通過合理地選擇Bean的作用域,可以優化應用的性能和資源利用率。例如,對于需要頻繁創建和銷毀實例的Bean,使用原型作用域會更高效;而對于需要在多個請求或會話之間共享狀態的Bean,則可以選擇單例或會話作用域。附官方圖:C8m28資訊網——每日最新資訊28at.com

圖片圖片C8m28資訊網——每日最新資訊28at.com

接下來將分別介紹每一種作用域bean。C8m28資訊網——每日最新資訊28at.com

二. 作用域應用

基礎類

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

2.1 單例(singleton)

默認使用@Bean,@Service,@Controller注解標注的注解都是單例的。也可以同@Scope注解指定作用域為單例C8m28資訊網——每日最新資訊28at.com

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

測試C8m28資訊網——每日最新資訊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)) ;}

控制臺輸出C8m28資訊網——每日最新資訊28at.com

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

每次獲取的都是同一個實例。C8m28資訊網——每日最新資訊28at.com

原理C8m28資訊網——每日最新資訊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實例。C8m28資訊網——每日最新資訊28at.com

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

控制臺輸出C8m28資訊網——每日最新資訊28at.com

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

每次獲取都是不同的對象。C8m28資訊網——每日最新資訊28at.com

原理C8m28資訊網——每日最新資訊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?C8m28資訊網——每日最新資訊28at.com

2.3 請求(request)

接下來都是與web環境相關了,所以這里演示的示例會以SpringBoot3.0.5環境演示。C8m28資訊網——每日最新資訊28at.com

基礎類C8m28資訊網——每日最新資訊28at.com

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

測試類C8m28資訊網——每日最新資訊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 ;  }}

ServiceC8m28資訊網——每日最新資訊28at.com

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

如果上面這樣配置,啟動服務將會報錯:C8m28資訊網——每日最新資訊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請求開始創建的,所以這里你當然是沒法注入的。C8m28資訊網——每日最新資訊28at.com

解決辦法:C8m28資訊網——每日最新資訊28at.com

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

測試結果C8m28資訊網——每日最新資訊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都是同一個。C8m28資訊網——每日最新資訊28at.com

  • 使用@RequestScope

該注解原理與上面其實一致的C8m28資訊網——每日最新資訊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 {}

測試C8m28資訊網——每日最新資訊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;你再換個瀏覽器訪問C8m28資訊網——每日最新資訊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了。C8m28資訊網——每日最新資訊28at.com

2.5 應用(application)

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

測試C8m28資訊網——每日最新資訊28at.com

360瀏覽器C8m28資訊網——每日最新資訊28at.com

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

Chrome瀏覽器C8m28資訊網——每日最新資訊28at.com

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

他們是同一個對象,application作用域生命周期與整個應用一樣,只有你關閉了服務器,在啟動后才會是再重新創建的bean對象。C8m28資訊網——每日最新資訊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。C8m28資訊網——每日最新資訊28at.com

3.2 查找web作用域beanC8m28資訊網——每日最新資訊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的作用域,可以優化應用的性能和資源利用率。C8m28資訊網——每日最新資訊28at.com

本文鏈接:http://www.www897cc.com/showinfo-26-57916-0.html玩轉Spring各種作用域Bean Scope及源碼分析

聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: MongoDB 大量數據插入時的性能影響及解決方法

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

標簽:
  • 熱門焦點
Top 主站蜘蛛池模板: 锦州市| 石渠县| 迭部县| 黑水县| 宝坻区| 赤水市| 正宁县| 郓城县| 嘉荫县| 永德县| 南平市| 和平区| 定陶县| 宜君县| 海城市| 黄梅县| 满洲里市| 邛崃市| 靖州| 明水县| 平湖市| 宜春市| 博罗县| 青神县| 乃东县| 宜川县| 大姚县| 五河县| 兴和县| 南宫市| 察隅县| 嵩明县| 咸宁市| 怀安县| 开远市| 延寿县| 酒泉市| 泰来县| 三明市| 桃园县| 上栗县|