为什么Spring Bean是单例模式?

8

我正在使用Hibernate和Spring进行工作,进展顺利,但我有一些疑问。

1)为什么Spring范围默认为单例?这样做有什么原因吗?

2)我可以在Hibernate实体中写final变量吗? 例如:

@Entity
public class Emp {
  @Id
  private Long id;
  final private String panNo;
}

我可以像上面那样写吗?

3) 静态变量可以被序列化吗?

2个回答

26
如果你仔细观察Spring,你会发现Spring帮助你编写服务,而不是数据对象。使用Spring时,您仍然需要管理自己的领域对象,无论是关系型数据对象还是普通POJO对象,并将它们作为输入传递给Spring管理的服务、存储库和控制器等。
因此,考虑到这一点,应该清楚为什么Spring的默认范围不是原型、会话或请求:我们不需要每次请求到来时都创建一个新的服务。但为什么是单例呢?当服务是无状态的时候,它是线程安全的,并且可以扩展到任意数量的并发请求,因此没有必要再创建一个相同服务的副本。
与EJB不同,其中有有状态和无状态bean,Spring只有一种类型的bean:无状态。如果您想管理状态,您需要自己处理。正如之前的回答已经指出的那样,无状态是更好的选择,因为它更快,更可伸缩和更易于维护,这也是REST架构所推广的。有状态bean在纸面上可能看起来很棒,但多年来已被证明是灾难性的。

1
Spring Bean 是单例模式,这不会影响编写多线程程序吗?这是我的想法,也是我使用原型作用域 Bean 的原因。 - pyetti
3
单例在多线程程序中不一定会出现问题,关键因素是有状态性和无状态性。没有实例变量的任何类都是线程安全的。只有线程安全的单例作为实例变量的单例(参见@Autowired和@Inject)也是线程安全的,您可以编写程序进行测试。 - Christopher Yang
1
我并不怀疑它是线程安全的。我的问题是关于需要访问相同Bean的单独线程,我想这会减慢处理速度,因为本质上有一个锁定该Bean的锁。不是吗? - pyetti
2
我猜当你说“不同的线程访问同一个bean”时,你是指在单个bean中访问相同的实例变量?与方法中的局部变量不同,实例变量存在于堆上,并且在方法调用后不会被垃圾回收。获取对这些变量的访问可能会引起争议并降低程序的活力。通过仅具有线程安全的单例和ThreadLocals作为实例变量,您基本上避免了争用(又称锁定),并强制状态必须在其他地方维护,例如客户端、分布式缓存或数据库。 - Christopher Yang
“一个容器中每个bean id只有一个bean”的想法如何?既然服务是无状态的,为什么Spring会为不同的ID创建不同的对象呢? - Bishwas Mishra

11

无状态的Bean规则:)如果你不打算在Bean中保存状态数据,那么每个Bean只需要一个实例就足够了。但请记住,这并不是JVM单例-只是Spring的单例。因此,您不必提供仅私有构造函数和任何getInstance()方法。

引用Spring文档:

当Bean是单例时,将管理一种共享的Bean实例,并且所有对具有匹配该Bean定义的ID或IDs的Bean的请求都将导致返回该特定Bean实例。

只有当您必须保留某些会话详细信息时,才应例如使用会话范围。


JVM单例和Spring单例有什么区别? - Koray Tugay
@KorayTugay https://dev59.com/XXE85IYBdhLWcg3wvGER - Jakub Kubrynski

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