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

时间:2010-04-14 13:47:45

标签: java spring singleton containers

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

如果我无法解释我实际上要问的内容,请耐心等待。

11 个答案:

答案 0 :(得分:54)

Spring中的单例bean和单例模式是完全不同的。单例模式表示每个类加载器将创建一个且只有一个特定类的实例。

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

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

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

答案 1 :(得分:28)

Spring中的Singleton范围意味着Spring上下文中的单个实例。
Spring容器只是一次又一次地返回相同的实例,以便后续调用获取bean。

如果将bean的类编码为singleton,则spring不会打扰,实际上如果该类被编码为singleton,其构造函数为private,Spring使用BeanUtils.instantiateClass(javadoc here)来设置构造函数可访问并调用它。

或者,我们可以像bean这样使用bean定义中的factory-method属性

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

答案 2 :(得分:18)

让我们举一个最简单的例子:你有一个应用程序,你只需使用默认的类加载器。你有一个类,无论出于什么原因,你决定它不应该在应用程序中有多个实例。 (想想有几个人在应用程序的各个部分上工作的场景。)

如果您不使用Spring框架,Singleton模式可确保应用程序中不会有多个类的实例。这是因为你不能通过执行'new'来实例化类的实例,因为构造函数是私有的。获取类实例的唯一方法是调用类的一些静态方法(通常称为“getInstance”),它总是返回相同的实例。

假设您在应用程序中使用Spring框架,只是意味着除了获取类实例的常规方法(返回类实例的新方法或静态方法)之外,您还可以问Spring为了获得该类的实例,Spring将确保无论何时向它请求该类的实例,它都将返回相同的实例,即使您没有使用Singleton模式编写该类。换句话说,即使该类具有公共构造函数,如果您总是向Spring请求该类的实例,Spring将仅在您的应用程序生命周期内调用该构造函数一次。

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

如果你真的想要一个类的单个实例,即使你在你的应用程序中使用Spring并将Spring中的类定义为单例,唯一的方法是使用Singleton模式实现类。这确保了将有一个实例,无论人们使用Spring获取实例还是绕过Spring。

答案 3 :(得分:10)

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

Singleton范围在技术上注意到单例设计模式。您不必将bean实现为单例,以便将它们放在单例范围内。

答案 4 :(得分:4)

我发现&#34;每个容器每个豆&#34;难以理解。我会说&#34;每个bean id一个bean&#34;。让我们有一个例子来理解它。我们有一个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&#34; id1&#34;来获取bean时,spring容器将创建一个bean,对其进行缓存并返回与id1相同的bean。如果我尝试使用id7获取它,那么将从Sample类创建另一个bean,每次使用id7引用它时都会缓存并返回。

Singleton模式不太可能。在Singlton模式中,每个类加载器总是创建一个对象。但是在春天,许多对象都是为同一个类创建的。但是在Spring中,使作为Singleton的范围为相同的id返回相同的对象。 Reference

答案 5 :(得分:2)

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

Singleton模式确保每个类加载器都会创建一个且只有一个特定类的实例,其中Spring单例bean的范围被描述为“每个bean的每个容器”。 Spring中的Singleton作用域意味着这个bean只能由Spring实例化一次。 Spring容器只是一次又一次地返回相同的实例,以便后续调用获取bean。

答案 6 :(得分: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的共享实例将   被管理,并且所有对具有一个或多个id的bean的请求都与之匹配   bean定义将导致该特定的bean实例被   由Spring容器返回

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

答案 7 :(得分:1)

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

答案 8 :(得分:1)

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

ArrayList

JAVA SINGLETON:

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

答案 9 :(得分:1)

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

答案 10 :(得分:0)

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

在我回答之前,我先说一下你可以做到。您可以将bean实现为Singleton设计模式,并使用Spring作为Spring singleton bean将其注入到客户端类中。

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

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