51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

单例设计模式与 Spring Boot 中的 Singleton Bean

1、概览 {#1概览}

单例对象经常被开发人员使用,因为应用程序中的许多对象都需要重复使用一个单例。在 Spring 中,我们可以通过 使用 Spring 的单例 Bean 或自己实现单例设计模式 来创建单例对象。

在本教程中,我们将首先了解单例设计模式及其线程安全的实现。然后,我们将了解 Spring 中的 Singleton Bean Scope,并将 singleton Bean 与使用单例设计模式创建的对象进行比较。

最后,我们将介绍一些可行的最佳实践。

本文中的 "Singleton Bean",即"单例 bean"。

2、单例设计模式 {#2单例设计模式}

单例是 "GoF" (si人帮?)于 1994 年发布的最简单的设计模式之一。它被归类于创建模式,因为单例提供了一种只创建一个实例的方法。

2.1、模式定义 {#21模式定义}

单例模式是指由一个类负责创建对象,并确保只创建一个实例。我们经常使用单例来共享状态或减少创建多个对象的成本。

单例模式实现可以确保只创建一个实例:

  • 通过实现单个私有构造函数来隐藏所有构造函数。
  • 仅在实例不存在时创建实例,并将其存储在私有静态变量中。
  • 使用公共静态 getter 方法访问该单例。

让我们看看几个使用单例对象的类的示例:

单件设计模式的类

在上面的类图中,我们可以看到多个服务如何使用只创建一次的同一个单例。

2.2、懒加载 {#22懒加载}

单例模式实现通常使用懒加载来延迟实例创建(也称为"懒汉式"),直到第一次实际需要时才创建。为了确保延迟实例化,我们可以在首次调用静态 getter 方法时创建实例:

public final class ThreadSafeSingleInstance {

    private static volatile ThreadSafeSingleInstance instance = null;

    private ThreadSafeSingleInstance() {}

    public static ThreadSafeSingleInstance getInstance() {
        if (instance == null) {
            synchronized(ThreadSafeSingleInstance.class) {
                if (instance == null) {
                    instance = new ThreadSafeSingleInstance();
                }
            }
        }
        return instance;
    }

    // 标准的 getter 方法

}

在多线程应用中,延迟加载可能会导致并发问题。因此,我们还应用了双重检查锁,以防止不同线程创建多个实例。

3、Spring 中的 Singleton Bean {#3spring-中的-singleton-bean}

Spring 框架中的 bean 是在 Spring IoC 容器中创建、管理和销毁的对象。

3.1、Bean Scope {#31bean-scope}

通过 Spring Bean,我们可以使用反转控制 (IoC) 通过元数据将对象注入 Spring 容器。实际上,一个对象可以定义其依赖,而无需创建它们,并将这项工作委托给 IoC 容器。

最新版本的 Spring 框架定义了六种 scope:

  • singleton
  • prototype
  • request
  • session
  • application
  • websocket

Bean 的 scope 定义了它的生命周期和可见性。它还决定了如何创建 bean 的实际实例。例如,我们可能想创建一个全局实例,或者每次请求 bean 时都创建一个不同的实例。

3.2、Singleton Bean {#32singleton-bean}

我们可以使用配置类中的 @Bean 注解在 Spring 中声明 Bean。Spring 中的 singleton scope 为容器中的每个 Bean 标识创建一个 Bean:

@Configuration
public class SingletonBeanConfig {

    @Bean
    @Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
    public SingletonBean singletonBean() {
        return new SingletonBean();
    }

}

Singleton 是 Spring 中定义的所有 Bean 的默认 scope。因此,即使我们不使用 @Scope 注解指定特定的 scope,我们仍然会得到一个单例 Bean。这里包含的 scope 仅供参考。它通常用于表达其他可用的 scope。

3.3、Bean 标识符(Identifier) {#33bean-标识符identifier}

与纯粹的单例设计模式不同,我们可以从同一个类中创建多个单例 Bean:

@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
public SingletonBean singletonBean() {
    return new SingletonBean();
}

@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
public SingletonBean anotherSingletonBean() {
    return new SingletonBean();
}

对具有匹配标识符的 Bean 的所有请求都将导致框架返回一个特定的 Bean 实例。当我们在方法上使用 @Bean 注解时,Spring 会将方法名称用作 Bean 标识符。

注入 Bean 时,如果容器中存在多个相同类型的 Bean,框架会抛出 NoUniqueBeanDefinitionException

@Autowired
private SingletonBeanConfig.SingletonBean bean; //抛出异常

在这种情况下,我们可以使用 @Qualifier 注解来指定要注入 Bean 的标识符:

@Autowired
@Qualifier("singletonBean")
private SingletonBeanConfig.SingletonBean beanOne;

@Autowired
@Qualifier("anotherSingletonBean")
private SingletonBeanConfig.SingletonBean beanThree;

另外,当存在多个相同类型的 Bean 时,还可以使用另一个注解 @Primary 来定义主 Bean(存在多个相同类型的 Bean 注入时,优先注入 @Primary 标识的 Bean)。

4、对比 {#4对比}

现在,让我们比较一下这两种方法,并总结出 Spring 的最佳实践。

4.1、单例反模式 {#41单例反模式}

有些人认为单例是一种反模式,因为它引入了应用程序级的全局状态。使用单例的任何其他对象都直接依赖于单例。这就造成了类和模块之间不必要的相互依赖。

单例模式还违反了单一责任原则。因为单例对象至少要对两件事负责:

  • 确保只创建一个实例。
  • 执行正常业务。

此外,在多线程环境中,单例需要特殊处理,以确保独立线程不会创建多个实例。它们还可能增加单元测试和 mock 的难度。由于许多 mock 框架依赖于继承,私有构造函数使得单例对象难以 mock。

4.2、推荐方法 {#42推荐方法}

使用 Spring 的单例 Bean 而不是实现单例设计模式,可以消除上述许多缺点。

Spring 框架在所有使用 Bean 的类中注入 Bean,但保留了替换或扩展 Bean 的灵活性。该框架通过保持对 bean 生命周期的控制来实现这一点。因此,以后可以用另一种方法替换它,而无需更改任何代码。

此外,Spring Bean 还让单元测试变得更加简单。Spring Bean 易于 mock,框架可以将其注入测试类。我们可以选择注入实际的 Bean 实现或它们的 mock。

我们应该注意的是,单例 bean 不会只创建一个类的实例,而是在容器中为每个 bean 标识符创建一个 bean。

5、总结 {#5总结}

在本文中,我们探讨了如何在 Spring 框架中创建单例。我们研究了单例设计模式的实现,以及如何使用 Spring 的 singleton Bean。

我们探索了如何通过懒加载和线程安全实现单例模式。然后,我们研究了 Spring 中的单例 Bean scope,并探索了如何实现和注入单例 Bean。我们还了解了单例 Bean 与使用单例设计模式创建的对象之间的区别。

最后,我们了解了在 Spring 中使用 singleton Bean 如何消除单例设计模式传统实现的一些缺点。


参考:https://www.baeldung.com/spring-boot-singleton-vs-beans

赞(1)
未经允许不得转载:工具盒子 » 单例设计模式与 Spring Boot 中的 Singleton Bean