繁体   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