众所周知,在Spring容器中,默认情况下我们拥有单例Bean,如果我们的Web应用程序基于Spring框架,则在这种情况下,我们是否真的需要实现Singleton设计模式来保存全局数据,而不仅仅是通过Spring创建一个Bean。
如果我没有能够解释清楚我的问题,请多包涵。
众所周知,在Spring容器中,默认情况下我们拥有单例Bean,如果我们的Web应用程序基于Spring框架,则在这种情况下,我们是否真的需要实现Singleton设计模式来保存全局数据,而不仅仅是通过Spring创建一个Bean。
如果我没有能够解释清楚我的问题,请多包涵。
在Spring中,单例模式和单例Bean是两个不同的概念。单例模式指的是对于每个类加载器只会创建一个特定类的实例。
Spring中单例Bean的范围被描述为“每个容器每个Bean”。它指的是每个Spring IoC容器中一个Bean定义对应一个对象实例。在Spring中,默认的范围是Singleton。
尽管默认的范围是Singleton,你可以通过指定<bean ../>
元素的scope属性来改变Bean的范围。
<bean id=".." class=".." scope="prototype" />
我发现 "每个容器每个bean" 难以理解。我会说 "一个容器中每个bean ID 对应一个bean"。让我们通过一个例子来理解它。我们有一个叫做 Sample 的bean类。我在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引用它时返回。
这与Singleton模式不太可能相同。在Singleton模式中,始终会为每个类加载器创建一个对象。但是在Spring中,将作用域设置为Singleton并不会限制容器从该类创建多个实例。它只是限制了对于相同ID的新对象创建,每当请求某个ID的对象时,都会返回先前创建的对象。参考资料
在Spring中,单例范围指的是一个Spring上下文中的单个实例。Spring容器仅针对后续调用获取bean时返回相同的实例。
并且Spring并不关心bean的类是否编码为单例。实际上,如果该类编码为私有单例构造函数,Spring会使用BeanUtils.instantiateClass(javadoc 这里)将构造函数设置为可访问并调用它。
或者,我们可以在bean定义中使用factory-method属性,如下所示
<bean id="exampleBean" class="example.Singleton" factory-method="getInstance"/>
让我们来看一个最简单的例子:你有一个应用程序,你只使用默认的类加载器。你有一个类,由于某些原因,你决定它在应用程序中不应该有多个实例。(想象一下有几个人在应用程序的各个部分上工作的情况)。
如果您没有使用 Spring 框架,Singleton 模式确保您的应用程序中不会有多个类的实例。这是因为您无法通过执行“new”来实例化类的实例,因为构造函数是私有的。获取类的实例的唯一方法是调用类的某个静态方法(通常称为“getInstance”),该方法始终返回相同的实例。
如果您在应用程序中使用 Spring 框架,这意味着除了获取类的实例的常规方式(返回类的实例的 new 或静态方法)之外,您还可以要求 Spring 获取该类的实例,并且 Spring 将确保每当您请求该类的实例时,它都将始终返回相同的实例,即使您没有使用 Singleton 模式编写类。换句话说,即使该类具有公共构造函数,如果您总是向 Spring 要求该类的实例,Spring 在应用程序的生命周期内只调用那个构造函数一次。
通常,如果您正在使用 Spring,则应仅使用 Spring 创建实例,并且可以为类使用公共构造函数。但是,如果您的构造函数不是私有的,则无法真正防止任何人绕过 Spring 直接创建类的新实例。
如果您确实想要一个类的单个实例,即使在应用程序中使用 Spring 并将该类定义为单例,也唯一的方法是还要使用 Singleton 模式来实现该类。这确保了只有一个实例,无论人们是否使用 Spring 来获取实例或绕过 Spring。
Spring 中的单例范围意味着这个 bean 只会被 Spring 实例化一次。与原型范围(每次新实例)、请求范围(每个请求一次)和会话范围(每个 HTTP 会话一次)不同。
单例范围在技术上与单例设计模式无关。您不必将您的 bean 实现为单例,它们也可以被放置在单例范围中。
这两者之间有非常根本的区别。在单例设计模式中,每个类加载器只会创建一个类的实例,而在Spring单例中,为给定ID创建共享bean实例的IoC容器不止一个。
例如,如果我有一个名为“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对象。 而单例模式确保每个类加载器只创建一个特定类的实例。