Spring Bean 的作用域(Bean Scope)

来源:https://www.cnblogs.com/god23bin/archive/2023/09/11/spring-bean-scope.html
-Advertisement-
Play Games

前言 大家好,我是 god23bin,今天我們來聊一聊 Spring 框架中的 Bean 作用域(Scope)。 什麼是 Bean 的作用域? 我們在以 XML 作為配置元數據的情況下,進行 Bean 的定義,是這樣的: <bean id="vehicle" class="cn.god23bin.d ...


前言

大家好,我是 god23bin,今天我們來聊一聊 Spring 框架中的 Bean 作用域(Scope)。

什麼是 Bean 的作用域?

我們在以 XML 作為配置元數據的情況下,進行 Bean 的定義,是這樣的:

<bean id="vehicle" class="cn.god23bin.demo.domain.model.Vehicle">
	<!-- 協作者寫在這裡... -->
</bean>

我們寫了一個 Bean 定義(Bean Definition),就是用於創建所定義的類的實例的。

一個 Bean 定義,我們可以類比一個類的定義,你定義了一個類,你可以根據這個類創建出許多實例對象。同理,Bean 定義也是,也是可以根據這個定義創建許多實例對象的,只不過這裡是 Spring 幫我們創建,而不是我們手動 new 這些 Bean 對象實例,我們可以理解為 Spring IoC 容器中的對象。

在寫 Bean 定義的過程中,我們可以控制各種 Bean 的依賴項和相應的值,將這些依賴項和值註入到 Bean 定義所創建的對象中。同理,這個過程也可以控制 Bean 定義創建的對象的 Scope(作用域)。Bean 的作用域定義了在容器中創建的 Bean 實例的生命周期以及在應用程式中的可見性。

6 種 Bean 的作用域

Spring 支持 6 種 Bean 的作用域,其中有 4 種是在 Web 應用下才能感知到的,如下表所示:

Scope 說明
singleton (預設情況下)每個 Spring IoC 容器將單個 Bean 定義的 Scope 指定為單個對象實例。
prototype 將單個 Bean 定義的 Scope 擴大到任意數量的對象實例。
request 將單個 Bean 定義的 Scope 擴大到單個 HTTP 請求的生命周期。也就是說,每個 HTTP 請求都有自己的 Bean 實例,該實例是在單個 Bean 定義的基礎上創建的。只在 Web 感知的 Spring ApplicationContext 的上下文中有效。
session 將單個 Bean 定義的 Scope 擴大到一個 HTTP 會話的生命周期。只在 Web 感知的 Spring ApplicationContext 的上下文中有效。
application 將單個 Bean 定義的 Scope 擴大到 ServletContext 的生命周期中。只在 Web 感知的 Spring ApplicationContext 的上下文中有效。
websocket 將單個 Bean 定義的 Scope 擴大到 WebSocket 的生命周期。只在 Web 感知的 Spring ApplicationContext 的上下文中有效。

1. Singleton Scope

singleton 作用域的 Bean,在 Spring IoC 容器中就有且僅有一個該類型的實例對象,也就是單例的。

預設情況下,我們在寫 Bean 定義的時候,不指定作用域的話,那麼這個 Bean 對象就是單例的。

<!-- 不寫 Bean 的作用域,預設作用域為單例 -->
<bean id="accountService" class="cn.god23bin.demo.service.DefaultAccountService"/>

<!-- 寫上作用域,這裡是冗餘的寫法,使用 scope 屬性 -->
<bean id="accountService" class="cn.god23bin.demo.service.DefaultAccountService" scope="singleton"/>

這個單例對象是存儲在一個緩存區域中的,在後續的請求或者引用中,Spring 就會返回這個緩存的對象。

實際上,Spring 中的單例的 Bean 對象是不同於 Gang of Four 設計模式中的所定義的單例模式的。

設計模式(Design Pattern)是前輩們對代碼開發經驗的總結,是解決特定問題的一系列套路。它不是語法規定,而是一套用來提高代碼可復用性、可維護性、可讀性、穩健性以及安全性的解決方案。

1995 年,GoF(Gang of Four,四人組/四人幫)合作出版了《設計模式:可復用面向對象軟體的基礎》一書,共收錄了 23 種設計模式,從此樹立了軟體設計模式領域的里程碑,人稱「GoF設計模式」。

設計模式中的單例模式是硬編碼的方式,以便每個 ClassLoader 只創建一個特定類的一個實例。

Spring 單例的範圍是指每個 IoC 容器的,不同 IoC 容器維護自己的 Bean 的單例對象

2. Prototype Scope

Bean 的作用域是 prototype,中文意思是原型,實際上這裡是省略了 non-singleton,這個作用域的全稱是 non-singleton prototype scope,即「非單例原型的作用域」。

顧名思義,這個作用域下的 Bean 不是單例的,意思就是說 Bean 是多例的,每一次的請求或者引用,都會創建一個新的 Bean 對象。

當然這裡的請求或者引用的意思是指,非單例原型的 Bean 被註入到另一個 Bean 中的時候(Bean 作為屬性被引用),或者我們直接通過容器的 getBean() 方法調用來請求它的時候,就會創建一個新的對象。

在 XML 中指定了這個 Bean 的作用域為 prototype

<bean id="accountService" class="cn.god23bin.demo.service.DefaultAccountService" scope="prototype"/>

在 prototype 作用域下的 Bean,Spring 是不會負責該 Bean 的銷毀周期中回調的方法的,如果該 Bean 擁有一些重要的資源,想在該 Bean 對象銷毀時釋放這些資源,那麼需要自定義 BeanPostProcessor(Bean 的後置處理器),它持有我們需要清理的 Bean 的引用。

在某些方面來說,在 prototype 作用域下的 Bean 的作用是代替 new 操作的。

其餘 4 種作用域

requestsessionapplicationwebsocket scope 只有在使用 Web 感知的 Spring ApplicationContext 實現(如 XmlWebApplicationContext)時才可用。

簡而言之,一般是在 Web 應用下,藉助 Spring 的 Web 模塊,就能使用這 4 種作用域。

如果你將這些 scope 與常規的 Spring IoC 容器(如 ClassPathXmlApplicationContext)一起使用,就會拋出一個 IllegalStateException,提示有未知的 Bean scope。

3. Request Scope

<bean id="loginController" class="cn.god23bin.demo.controller.LoginController" scope="request"/>

Spring IoC 容器為每一個 HTTP 請求使用 loginController Bean 定義來創建 LoginController Bean 的新實例,從而實現這種 request 作用域。

你可以隨心所欲地改變被創建的實例的內部狀態,因為從同一個 loginController Bean 定義中創建的其他實例不會看到這些狀態的變化。它們是針對單個請求的,當請求完成處理時,該請求所涉及的 Bean 會被丟棄。

4. Session Scope

<bean id="userPreferences" class="cn.god23bin.demo.UserPreferences" scope="session"/>

Spring IoC 容器通過使用 userPreferences Bean 定義,在單個HTTP Session 的生命周期內創建一個新的 UserPreferences Bean 實例。

request scope 的 Bean 一樣,你可以隨心所欲地改變被創建的實例的內部狀態,要知道其他 HTTP Session 實例也在使用從同一個 userPreferences Bean定義中創建的實例,它們不會看到這些狀態的變化,因為它們是特定於單個HTTP Session。當HTTP Session 最終被丟棄時,作用於該特定HTTP Session 的 Bean 也被丟棄。

5. Application Scope

<bean id="appPreferences" class="cn.god23bin.demo.AppPreferences" scope="application"/>

Spring 容器通過為整個Web應用程式使用一次 appPreferences Bean 定義來創建 AppPreferences Bean的新實例。

這有點類似於Spring的 singleton Bean,但在兩個重要方面有所不同。

它是每個 ServletContext 的單例,而不是每個 Spring ApplicationContext(在任何給定的Web應用程式中可能有幾個)。

6. WebSocket Scope

這裡就涉及到 WebSocket 了,目前先不討論。後面再來填坑~

不同作用域的 Bean 之間的依賴關係

這裡討論的,一般就是單例作用域的 Bean原型作用域的 Bean 之間的依賴關係。

現在舉個例子,假設有兩個 Java 類交給了 Spring IoC 容器管理,分別是 SingletonBean 類和 PrototypeBean 類。

其中 SingletonBean 是單例作用域的 Bean,而 PrototypeBean 是原型作用域的 Bean。

那麼當:

  1. SingletonBean 的依賴項是 PrototypeBean 時,PrototypeBean 對象只會初始化一次並註入到 SingletonBean,這樣 PrototypeBean 就起不到原型作用域的效果。
  2. PrototypeBean 的依賴項是 SingletonBean 時,每次 PrototypeBean 對象都會創建,這些對象都依賴於一個單例對象,此時沒任何問題。

方法註入

Spring 提供了一種稱為方法註入(Method Injection)的機制來解決原型作用域的 Bean 在被註入到單例作用域的 Bean 中時只創建一個實例的問題。

方法註入允許每次調用方法時都獲取一個新的原型作用域的 Bean 實例

方法註入是通過在 SingletonBean 中定義一個返回 PrototypeBean 實例的方法來實現的。這樣,在每次需要使用 PrototypeBean 的地方,可以通過調用該方法獲取一個新的實例。

以下是使用方法註入解決 Prototype Bean 作用域的示例:

public abstract class SingletonBean {
    public abstract PrototypeBean getPrototypeBean();

    public void doSomething() {
        PrototypeBean prototypeBean = getPrototypeBean();
        // 使用 Prototype Bean 進行操作
    }
}

public class PrototypeBean {
    // Prototype Bean 的定義
}

在上述示例中,SingletonBean 是一個抽象類,其中聲明瞭一個抽象方法 getPrototypeBean(),該方法返回一個 PrototypeBean 實例。在 doSomething() 方法中,通過調用 getPrototypeBean() 方法獲取一個新的 PrototypeBean 實例,以便在每次調用 doSomething() 時使用不同的實例。

然後,可以通過具體的子類來實現 SingletonBean,並實現 getPrototypeBean() 方法以返回相應的 PrototypeBean 實例。

通過方法註入,每次調用 doSomething() 方法時都會獲取一個新的 PrototypeBean 實例,從而解決了在 Singleton Bean 中註入 Prototype Bean 時只創建一個實例的問題。

需要註意的是,方法註入需要在配置文件或使用註解時進行特殊的配置,具體的配置方式基本如下。

1. XML 配置方式

當然,上面舉例是一個抽象類,不是抽象類也是可以的,比如:

public class SingletonBean {
    // 方法註入,Spring 會幫我們返回這個對象,這裡寫成 null 即可
    public PrototypeBean getPrototypeBean() {
        return null;
    }

    public void doSomething() {
        PrototypeBean prototypeBean = getPrototypeBean();
        // 使用 Prototype Bean 進行操作
    }
}

public class PrototypeBean {
    // Prototype Bean 的定義
}

接著,單獨上面是沒有實現不了方法註入的,還需要結合配置元數據,現在在 XML 配置文件中使用 <lookup-method /> 標簽來實現方法註入。

<bean id="singletonBean" class="cn.god23bin.demo.domain.model.SingletonBean">
    <lookup-method name="getPrototypeBean" bean="prototypeBean"/>
</bean>

<bean id="prototypeBean" class="cn.god23bin.demo.domain.model.PrototypeBean" scope="prototype"/>

上面的配置示例中,singletonBean 是一個單例 Bean,通過 <lookup-method /> 標簽指定了一個名為 getPrototypeBean 的方法,並引用了一個原型 Bean prototypeBean

在運行時,每次調用 getPrototypeBean 方法時,都會返回一個新的 prototypeBean 實例。

2. 註解配置方式

使用 @Lookup 註解來實現方法註入。

@Component
public class SingletonBean {

    private PrototypeBean prototypeBean;

    @Lookup
    public PrototypeBean getPrototypeBean() {
        return null; // 實際上會由 Spring 生成具體實現
    }

    // 其他代碼...
}

@Component
@Scope("prototype")
public class PrototypeBean {
    // 具體的原型 Bean 實現
}

在上面的示例中,SingletonBean 使用了 @Lookup 註解標記了一個名為 getPrototypeBean 的方法。在運行時,Spring 會為這個方法生成具體的實現,以實現方法註入。

總結

簡單總結下:

Bean 的作用域在 Bean 定義的時候可以進行指定,預設是單例的,多例的 Bean 就是所謂的原型作用域。

一共 6 種作用域需要熟悉,其中 4 種是在具有 Web 感知能力的 Spring IoC (應用上下文)下才有的作用域。

對於單例 Bean 依賴原型 Bean 的問題,可以通過方法註入解決,兩種寫法實現方法註入,一種是 XML,另一種是註解的方式。

最後的最後

希望各位屏幕前的靚仔靚女們給個三連!你輕輕地點了個贊,那將在我的心裡世界增添一顆明亮而耀眼的星!

咱們下期再見!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 葉正盛,玖章算術 CEO葉正盛,NineData 創始人 &CEO,原阿裡雲資料庫產品管理與解決方案部總經理,資深資料庫專家玖章算術是基礎技術領域的科技創新企業,創始團隊來自阿裡、華為等公司多位 P9 級資深專家,旗下的核心產品 NineData 致力於讓每個人都能充分利用數據和雲計算技術。Post... ...
  • 引文 在資料庫運維過程中,所使用的運維管理平臺是否存在這樣的問題: 1、預設監控粒度不夠,業務需要更細顆粒度的監控數據。 2、平臺預設的監控命令不適合,需要調整閾值量身定製監控策略。 3、不同類型的實例或組件需要有不同的監控重點,但管理平臺監控固化,難以應對多樣化的監控需求。 4、只監控系統關鍵指標 ...
  • 項目結構 my-vue3-project ├─ .env //預設環境變數 ├─ .env.development //開發環境變數 ├─ .eslintrc-auto-import.json //(autoimport變數,eslint配置)由auto-import插件生成 ├─ .eslintr ...
  • 介紹 ESLint 是一個根據方案識別並報告 ECMAScript/JavaScript 代碼問題的工具,其目的是使代碼風格更加一致並避免錯誤。在很多地方它都與 JSLint 和 JSHint 類似,除了: ESLint 使用 Espree 對 JavaScript 進行解析。 ESLint 在代碼 ...
  • 1. 安裝 vue-i18n npm i vue-i18n -S 2. 創建一個i8n的配置文件 如:i18nConfig.js // 配置 vue-i18n 實現國際化語言設置 import { createI19n } from 'vue-i18n' import zh_cn from '../ ...
  • 最近系統學習了vue.js 的官方腳手架create-vue的源碼,深入分析了裡面的技術實現細節,本文是我整理的源碼學習文章。 ...
  • 很早之前,就寫過一篇與原生嵌套相關的文章 -- CSS 即將支持嵌套,SASS/LESS 等預處理器已無用武之地?,彼時 CSS 原生嵌套還處於工作草案 Working Draft (WD) 階段,而今天(2023-09-02),CSS 原生嵌套 Nesting 終於成為了既定的規範! CSS 原生 ...
  • 淺拷貝 當我們想要複製一段數據的時候嗎,我們就會用到拷貝;拷貝數據又分為了淺拷貝和深拷貝,淺拷貝指複製對象或數組的頂層結構,如果對象或數組中有引用類型的屬性值,複製的是引用(地址)而非值;而深拷貝則是遞歸複製完整的對象或數組,包括嵌套的子對象或子數組,生成一個全新的對象,新對象和原對象的引用地址不同 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...