Spring单例与Java单例设计模式有何区别?

28

我正在学习Spring框架,并且正在阅读一本相关的书籍。在这本书中提到,Spring单例与Java单例不同。这是什么意思,有哪些区别?谢谢。


你已经很好地理解了与其他响应的区别,但是我认为不想使用Java单例模式的原因之一是可测试性。当你编写单元测试时,单例累积状态信息会导致问题吗? - Ustaman Sangat
2个回答

36

Java的单例模式是由Java类加载器作用域限定的,而Spring的单例模式是由容器上下文作用域限定的。

这基本意味着,在Java中,你只能确保一个单例模式在加载它的类加载器上下文中是真正的单例模式。其他类加载器可以创建另一个该类的实例(前提条件是这些类加载器不在同一个类加载器层次结构中),即使你在代码中尝试了各种努力防止这种情况的发生。

在Spring中,如果您把单例类加载到两个不同的上下文中,那么就可能再次打破单例模式的概念。

因此,总之,Java认为如果在给定的类加载器中不能创建该类的多个实例,则认为该类是单例模式,而Spring则认为如果在给定的容器/上下文中不能创建该类的多个实例,则认为该类是单例模式。


如果您在同一bean定义/配置中使用不同的bean ID声明相同的类,则将在“上下文”中拥有两个不同的相同类实例。如果开发人员意图在上下文/容器中使用单例,则这可能被解释为开发人员配置错误。Spring不能因未确保单例而受到责备。如果我理解有误,请纠正我。 - user104309
1
即使您声明了两次相同的bean,它也只会被创建一次。Spring足够智能,可以处理这个问题,当您尝试通过ID获取实例时,您可以进行“==”比较并自行测试。 - nanosoft
2
@Edwin Dalorzo:我看到你在解释Spring单例时使用了“容器/上下文”,这可能会让一些人感到困惑。因为Spring Bean相对于容器而言是单例的,但相对于上下文(即应用程序)可能不是。 - nanosoft

4

Java的单例是一种设计模式,通过代码将实例化限制为一个(通常是每个类加载器)。 维基百科

Spring的单例bean可以是您编写的任何普通类,但将其范围声明为单例意味着Spring只会创建一个实例并将其引用提供给引用所声明的所有bean。您的应用程序中可能有许多该类的实例,但仅为该bean创建一个。您甚至可以将同一类的多个bean都声明为单例。每个bean将创建该类的正好一个实例。Spring 3.1文档


4
+1,尽管您陷入了同一段落中写“Spring单例类”的陷阱,而解释Spring只有单例bean,而没有单例。;-) - ruakh
1
实际上是由类加载器而不是JVM来控制的。一个JVM可能有多个类加载器,每个类加载器都可以拥有单例类的一个实例(前提是它们不在同一个类加载器层次结构中)。 - Edwin Dalorzo
@EdwinDalorzo:没错,但是如果它们在不同的类加载器中,那么它们可以说是不同的类。(JVM 标识“类”为一个类加载器加上一个完全限定的类名。) - ruakh
@ruakh 是的,但术语可能会令人困惑。我们还将给定类的二进制表示称为类。因此,“类”一词可能指其二进制表示或由JVM创建并与类加载器相关联的经过验证和链接的对象。根据您的评论,由两个不同类加载器加载的类二进制文件不是相同的“逻辑类”,我认为这是正确的。基于此,我们可以说C. Ross的原始陈述可能是正确的。但是如果没有这个解释,它将完全具有误导性。 - Edwin Dalorzo
1
@EdwinDalorzo:我想我并不认为这是关于单例类的事实,而是更一般地涉及到JVM中的类和类加载器的事实。请注意,如果它们来自不同的类加载器,则a.getClass() == b.getClass()将为false。(此外,您可以拥有一个“单例类”,即使在单个类加载器中也有多个实例,出于各种原因。) - ruakh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接