簡體   English   中英

單例設計模式與 Spring 容器中的單例 bean

[英]Singleton design pattern vs Singleton beans in Spring container

眾所周知,我們在 Spring 容器中默認將 bean 作為單例,如果我們有一個基於 Spring 框架的 Web 應用程序,那么在這種情況下,我們是否真的需要實現單例設計模式來保存全局數據,而不僅僅是通過 spring 創建一個 bean .

如果我無法解釋我真正想問的是什么,請耐心等待。

Spring 中的單例 bean 和單例模式完全不同。 單例模式表示,每個類加載器將永遠創建一個且只有一個特定類的實例。

Spring 單例的范圍被描述為“每個容器每個 bean”。 它是 bean 定義的范圍到每個 Spring IoC 容器的單個對象實例。 Spring 中的默認范圍是 Singleton。

即使默認范圍是單例,您也可以通過指定<bean ../>元素的 scope 屬性來更改 bean 的范圍。

<bean id=".." class=".." scope="prototype" />

spring 中的單例范圍意味着 Spring 上下文中的單個實例..
Spring 容器只是一次又一次地返回相同的實例,以便后續調用獲取 bean。


如果 bean 的類是否編碼為單例,spring 不會在意,事實上,如果類編碼為單例,其構造函數為私有,則 Spring 使用 BeanUtils.instantiateClass( 此處為 javadoc)將構造函數設置為可訪問並調用它。

或者,我們可以像這樣在 bean 定義中使用 factory-method 屬性

    <bean id="exampleBean" class="example.Singleton"  factory-method="getInstance"/>

讓我們舉一個最簡單的例子:你有一個應用程序,你只使用默認的類加載器。 您有一個類,無論出於何種原因,您決定它在應用程序中不應有多個實例。 (想想幾個人在應用程序的各個部分工作的場景)。

如果您沒有使用 Spring 框架,單例模式可確保您的應用程序中不會有多個類的實例。 那是因為您不能通過執行 'new' 來實例化類的實例,因為構造函數是私有的。 獲取類實例的唯一方法是調用類的一些靜態方法(通常稱為“getInstance”),它總是返回相同的實例。

說你在你的應用程序中使用的是Spring框架,只是意味着除了常規的獲取類實例的方式(返回類的實例的新方法或靜態方法)之外,你還可以要求Spring獲取你該類的實例和 Spring 將確保每當您向它請求該類的實例時,它始終會返回相同的實例,即使您沒有使用單例模式編寫該類。 換句話說,即使該類有一個公共構造函數,如果您總是向 Spring 請求該類的實例,則 Spring 在您的應用程序生命周期中只會調用該構造函數一次。

通常,如果您使用 Spring,您應該只使用 Spring 創建實例,並且您可以為該類創建一個公共構造函數。 但是如果你的構造函數不是私有的,你並沒有真正阻止任何人通過繞過 Spring 直接創建類的新實例。

如果您真的想要該類的單個實例,即使您在應用程序中使用 Spring 並將 Spring 中的類定義為單例,確保也是使用單例模式實現該類的唯一方法。 這確保了將有一個實例,無論人們使用 Spring 獲取實例還是繞過 Spring。

我發現“每個容器每個豆子”很難理解 我會說“容器中每個 bean id 一個 bean ”。讓我們舉一個例子來理解它。 我們有一個 bean 類 Sample。 我在 bean 定義中定義了這個類中的兩個 bean,例如:

<bean id="id1" class="com.example.Sample" scope="singleton">
        <property name="name" value="James Bond 001"/>    
</bean>    
<bean id="id7" class="com.example.Sample" scope="singleton">
        <property name="name" value="James Bond 007"/>    
</bean>

因此,當我嘗試獲取 id 為“id1”的 bean 時,spring 容器將創建一個 bean,緩存它並返回與 id1 引用的相同 bean。 如果我嘗試使用 id7 獲取它,則會從 Sample 類創建另一個 bean,每次使用 id7 引用它時,都會緩存並返回相同的 bean。

單例模式不太可能出現這種情況。 在 Singlton 模式中,每個類加載器總是創建一個對象。 但是在 Spring 中,將作用域設為 Singleton 並不會限制容器從該類創建許多實例。 它只是再次限制為同一 ID 創建新對象,當為相同 id 請求對象時返回先前創建的對象 參考

Spring 中的單例作用域意味着這個 bean 只會被 Spring 實例化一次。 對比原型范圍(每次都有新實例)、請求范圍(每個請求一次)、會話范圍(每個 HTTP 會話一次)。

單例范圍在技術上與單例設計模式無關。 您不必將 bean 實現為單例,以便將它們放入單例范圍。

兩者之間有非常根本的區別。 在 Singleton 設計模式的情況下,每個 classLoader 只會創建一個類的實例,而 Spring 單例則不是這種情況,因為稍后會為每個 IoC 容器創建一個給定 id 的共享 bean 實例。

例如,如果我有一個名為“SpringTest”的類,而我的 XML 文件如下所示:-

<bean id="test1" class="com.SpringTest" scope="singleton">
        --some properties here
</bean>    
<bean id="test2" class="com.SpringTest" scope="singleton">
        --some properties here   
</bean>

所以現在在主類中,如果您將檢查上述兩個的引用,它將根據 Spring 文檔返回 false:-

當 bean 是單例時,只會管理該 bean 的一個共享實例,並且所有對具有與該 bean 定義匹配的 id 或 ids 的 bean 的請求將導致 Spring 容器返回該特定 bean 實例

所以在我們的例子中,類是相同的,但我們提供的 id 是不同的,因此導致創建了兩個不同的實例。

Spring 中的單例 bean 和基於單例設計模式的類有很大不同。

單例模式確保每個類加載器只創建一個特定類的一個實例,其中 Spring 單例 bean 的范圍被描述為“每個容器每個 bean”。 Spring 中的單例作用域意味着這個 bean 只會被 Spring 實例化一次。 Spring 容器只是一次又一次地返回相同的實例,以便后續調用獲取 bean。

Spring 單例 bean 被描述為“每個容器每個 bean”。 Spring 中的單例范圍意味着相同內存位置的相同對象將返回到相同的 bean id。 如果一個人創建了同一個類的不同 id 的多個 bean,那么容器將返回不同的對象到不同的 id。 這就像一個鍵值映射,其中鍵是 bean id,值是一個 spring 容器中的 bean 對象。 單例模式確保每個類加載器只創建一個特定類的一個實例。

至少到目前為止,所有答案都集中在解釋設計模式和 Spring 單例之間的區別上,並沒有解決您的實際問題:應該使用單例設計模式還是 Spring 單例 bean? 什么是更好的?

在我回答之前,讓我先聲明您可以同時執行這兩項操作。 您可以將 bean 實現為單例設計模式,並使用 Spring 將其作為 Spring 單例 bean 注入客戶端類。

現在,問題的答案很簡單:不要使用單例設計模式!
使用 Spring 的單例 bean 實現為具有公共構造函數的類。
為什么? 因為 Singleton 設計模式被認為是一種反模式。 主要是因為它使測試復雜化。 (如果你不使用 Spring 來注入它,那么所有使用單例的類現在都緊緊地綁定到它),並且你不能替換或擴展它。 可以谷歌“單例反模式”以獲得更多信息,例如單例反模式

使用 Spring 單例是要走的路(單例 bean 實現不是作為單例設計模式,而是使用公共構造函數),以便可以輕松測試 Spring 單例 bean,並且使用它的類不會與它緊密耦合,而是 Spring 將單例(作為接口)注入到所有需要它的 bean 中,並且可以隨時將單例 bean 替換為另一個實現,而不會影響使用它的客戶端類。

例如:“每個容器每個豆”。

        <bean id="myBean" class="com.spring4hibernate4.TestBean">
            <constructor-arg name="i" value="1"></constructor-arg>
            <property name="name" value="1-name"></property>
        </bean>

        <bean id="testBean" class="com.spring4hibernate4.TestBean">
            <constructor-arg name="i" value="10"></constructor-arg>
            <property name="name" value="10-name"></property>
        </bean>
    </beans>



    public class Test {

        @SuppressWarnings("resource")
        public static void main(String[] args) {
            ApplicationContext ac = new ClassPathXmlApplicationContext("ws.xml");
            TestBean teatBean = (TestBean) ac.getBean("testBean");
            TestBean myBean1 = (TestBean) ac.getBean("myBean");
            System.out.println("a : " + teatBean.test + " : "   + teatBean.getName());
            teatBean.setName("a TEST BEAN 1");
            System.out.println("uPdate : " + teatBean.test + " : "  + teatBean.getName());
            System.out.println("a1 : " + myBean1.test + " : " + myBean1.getName());
            myBean1.setName(" a1 TEST BEAN 10");
            System.out.println("a1 update : " + teatBean.test + " : " + myBean1.getName());
        }
    }

public class TestBean {
    public int test = 0;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    private String name = "default";

    public TestBean(int i) {
        test += i;
    }
}

爪哇單例:

public class Singleton {
    private static Singleton singleton = new Singleton();
    private int i = 0;

    private Singleton() {
    }

    public static Singleton returnSingleton() {

        return singleton;
    }

    public void increment() {
        i++;
    }

    public int getInt() {
        return i;
    }
}

public static void main(String[] args) {
        System.out.println("Test");

        Singleton sin1 = Singleton.returnSingleton();
        sin1.increment();
        System.out.println(sin1.getInt());
        Singleton sin2 = Singleton.returnSingleton();
        System.out.println("Test");
        sin1.increment();
        System.out.println(sin1.getInt());
    }

spring中的“singleton”是使用bean factory獲取實例,然后緩存它; 哪個單例設計模式是嚴格的,實例只能從靜態get方法中獲取,並且對象永遠不能被公開實例化。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM