Spring容器中的Singleton设计模式与Singleton bean


90

众所周知,默认情况下,我们在Spring容器中将bean作为单例,如果我们有一个基于Spring框架的Web应用程序,那么在这种情况下,我们真的需要实现Singleton设计模式来保存全局数据,而不仅仅是通过spring创建bean 。

如果我无法解释我实际上要问的问题,请忍受。

Answers:


59

Spring中的单例豆和单例模式有很大不同。Singleton模式表示,每个类加载器将只创建一个特定类的一个实例。

Spring单例的范围被描述为“每个容器每个bean”。这是每个Spring IoC容器的单个对象实例的bean定义范围。Spring的默认范围是Singleton。

即使默认范围是单例,您也可以通过指定<bean ../>element 的scope属性来更改bean的范围。

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

12
@ user184794:每个容器每个bean,这意味着spring容器中只有一个类加载器。如果spring容器中有两个或更多类加载器,则每个类加载器都有自己的实例。这是否意味着“每个容器每个类装入器每个bean每个容器”。请澄清!
Dead Programmer,

4
我认为这意味着Spring容器将使用它拥有的单个类加载器。在Spring机制之外进行的操作无关紧要,例如,您可以创建自己的类加载器,并根据需要创建一个类的实例,但是如果通过Spring容器,它将不会创建多个实例
inor

1
然后,它们不会像您所说的那样“完全不同”。唯一的区别是范围– Spring容器和classloader
Zack Macomber

30

Spring中的Singleton范围表示Spring上下文中的单个实例
。Spring容器仅一次又一次返回同一实例,以供后续调用以获取Bean。


而且,无论bean的类是否被编码为单例,spring都不会打扰;实际上,如果该类被编码为构造函数为私有的单例,Spring则使用BeanUtils.instantiateClass(此处为Javadoc )将构造函数设置为可访问并调用它。

另外,我们可以像这样在bean定义中使用factory-method属性

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

1
确定要使用factory-method属性吗?我很确定,即使构造函数是私有的,Spring也知道如何获取实例(可能尝试调用getInstance)
2014年

有关Spring如何在此处
Xiawei Zhang

21

让我们以最简单的示例为例:您拥有一个应用程序,而仅使用默认的类加载器。您有一个类,无论出于何种原因,您都决定该类在应用程序中不应有多个实例。(考虑一个由几个人共同处理应用程序的场景)。

如果您不使用Spring框架,则Singleton模式可确保您的应用程序中一个类的实例不超过一个。那是因为构造函数是私有的,因此无法通过执行“ new”来实例化类的实例。获取该类实例的唯一方法是调用该类的某些静态方法(通常称为“ getInstance”),该方法始终返回相同的实例。

说您在应用程序中使用Spring框架,仅意味着除了获取类实例的常规方法(返回类实例的新方法或静态方法)之外,您还可以要求Spring协助您该类的实例,Spring会确保即使您未使用Singleton模式编写该类,只要您向其要求该类的实例,它将始终返回相同的实例。换句话说,即使该类具有公共构造函数,如果您始终向Spring请求该类的实例,则Spring在应用程序生命周期内只会调用该构造函数一次。

通常,如果使用Spring,则应仅使用Spring创建实例,并且可以为该类提供一个公共构造函数。但是,如果您的构造函数不是私有的,那么您实际上并不会通过绕过Spring来阻止任何人直接创建该类的新实例。

如果您确实想要该类的单个实例,即使您在应用程序中使用Spring并将Spring中的类定义为单例,那么确保该类的唯一方法就是也使用Singleton模式实现该类。这样可以确保只有一个实例,无论人们使用Spring获取实例还是绕过Spring。


13

我发现“ 每个容器每个豆”很难理解。我会说“ 容器中的每个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的对象时,将返回先前创建的对象参考


好解释。谢谢!
Swapnil

12

Spring中的Singleton作用域意味着该bean将仅由Spring实例化一次。与原型范围(每次都有新实例)相反,请求范围(每个请求一次),会话范围(每个HTTP会话一次)。

从技术上讲,单例作用域与单例设计模式无关。您不必将bean实施为单例即可将它们放入单例范围。


1
如果我错了,请更正我,因此根据您的需要,如果我需要将任何对象实现为单例,则无需实现单例模式。使用Spring创建该bean将起作用。现在,我对与Spring框架中的Singleton设计模式和Singleton范围有关的理解有些困惑。
Peeyush

1
Spring不会强迫您使用Singleton模式。
lexicore

2

Spring中的Singleton bean和基于Singleton设计模式的类完全不同。

单例模式可确保每个类加载器都只能创建一个特定类的一个实例,而Spring单例bean的范围被描述为“每个容器每个容器”。Spring中的Singleton作用域意味着该bean将仅由Spring实例化一次。Spring容器仅一次又一次返回相同的实例,以供后续调用以获取Bean。


13
您是“ java特立独行者”,对吗?那将使您的声明“在...处找到了很好的解释和例子”,这是一种不诚实的尝试,掩盖了您正在链接到自己的网站。无论如何,您的链接对于答案似乎并不重要。我将其删除,以避免将答案删除为垃圾邮件。在发布更多链接到您的网站之前,请阅读关于自我促销的常见问题解答。还请注意,将网站链接放在个人资料中也可以。
安德鲁·巴伯

2

两者之间有一个非常根本的区别。在使用Singleton设计模式的情况下,每个classLoader仅创建一个类的实例,而对于Spring Singleton,情况并非如此,因为在稍后的情况下,将为每个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或id的bean的请求都将导致Spring容器返回一个特定的bean实例。

因此,就像我们的情况一样,这些类是相同的,但是我们提供的id是不同的,因此导致创建了两个不同的实例。


1

春天的“单例”是使用bean工厂的get实例,然后将其缓存;严格来说,这是单例设计模式,只能从静态get方法中检索实例,并且永远不能公开实例化该对象。


1

EX:“每个容器每个豆”。

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

JAVA SINGLETON:

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

<bean class =“ com.spring4hibernate4.TestBean”> <constructor-arg name =“ i” value =“ 1”> </ constructor-arg> <property name =“ name” value =“ 1-name”> </ property> </ bean> <bean class =“ com.spring4hibernate4.TestBean”> <constructor-arg name =“ i” value =“ 10”> </ constructor-arg> <property name =“ name” value =“ 10 -name“> </ property> </ bean> </ beans>
Hariprasad

1

春季单例bean被描述为“每个容器每个容器”。Spring中的Singleton作用域意味着在相同内存位置的同一对象将返回到相同的bean id。如果创建多个具有相同类的不同ID的bean,则容器将使不同的对象返回不同的id。这就像一个键值映射,其中key是bean id,value是一个spring容器中的bean对象。其中,Singleton模式可确保每个类加载器只能创建一个特定类的一个实例。


1

到目前为止,所有答案至少都集中于解释设计模式和Spring单例之间的区别,而没有解决您的实际问题:应该使用Singleton设计模式还是使用Spring单例bean?什么是更好的?

在我回答之前,请允许我先声明一下,您可以同时进行。您可以将bean实现为Singleton设计模式,并使用Spring作为Spring singleton bean将其注入到客户端类中。

现在,问题的答案很简单:不要使用Singleton设计模式!
使用Spring的singleton bean作为带有公共构造函数的类来实现。
为什么?因为Singleton设计模式被认为是反模式。主要是因为它使测试变得复杂。(如果您不使用Spring注入它,那么所有使用单例的类现在都将紧紧地绑定到它),并且您不能替换或扩展它。可以在Google上搜索“ Singleton反模式”以获得更多信息,例如Singleton反模式

使用Spring singleton是可行的方法(使用Singleton bean不是作为Singleton设计模式实现的,而是使用公共构造函数实现的),以便可以轻松地测试Spring singleton bean,并且使用该类的类不会紧密耦合到它,但是,Spring将单例(作为接口)注入到需要它的所有Bean中,并且可以随时用另一种实现替换该单例Bean,而不会影响使用它的客户端类。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.