Java基础:单例模式,Spring源码中有哪些单例模式

单例模式是一种常用的软件设计模式,其目的是确保一个类仅有一个实例,并提供一个全局访问点来获取这个唯一实例。在Java中,实现单例模式通常需要遵循以下几个关键原则:

  1. 私有化构造器:将类的构造器声明为private,以防止外部代码通过new操作符直接创建该类的实例。

  2. 静态工厂方法:提供一个静态方法(通常称为getInstance()),用于获取单例对象。这个方法负责检查是否已经创建过实例,如果尚未创建,则创建并保存;如果已存在,则直接返回该实例。

  3. 确保线程安全:在多线程环境中,必须确保单例对象的创建过程是线程安全的,即无论何时何地,无论多少个线程同时请求,都只会创建一个实例。

以下是几种常见的Java单例模式实现方式及其示例:

1. 饿汉式(静态常量)

优点:类加载时即初始化单例,线程安全,无同步开销。

缺点:如果单例实例很庞大或创建过程耗时,可能会导致类加载时较长的初始化时间,且即使从未使用该单例,也会占用内存。

public class SingletonEager {
    private static final SingletonEager INSTANCE = new SingletonEager();

    private SingletonEager() {}

    public static SingletonEager getInstance() {
        return INSTANCE;
    }
}

2. 懒汉式(线程不安全)

优点:延迟初始化,只有在首次调用getInstance()时才创建单例。

缺点:线程不安全,多线程环境下可能创建多个实例。

public class SingletonLazyUnsafe {
    private static SingletonLazyUnsafe instance;

    private SingletonLazyUnsafe() {}

    public static SingletonLazyUnsafe getInstance() {
        if (instance == null) {
            instance = new SingletonLazyUnsafe();
        }
        return instance;
    }
}

3. 懒汉式(线程安全,同步方法)

优点:解决了线程安全问题。

缺点:每次调用getInstance()都会进行同步,造成不必要的性能损耗。

public class SingletonLazySynchronizedMethod {
    private static SingletonLazySynchronizedMethod instance;

    private SingletonLazySynchronizedMethod() {}

    public static synchronized SingletonLazySynchronizedMethod getInstance() {
        if (instance == null) {
            instance = new SingletonLazySynchronizedMethod();
        }
        return instance;
    }
}

4. 双重检查锁定(DCL, Double-Checked Locking)

优点:既实现了延迟初始化,又保证了线程安全,且仅在初始化时加锁,性能较高。

缺点:依赖JVM正确实现内存模型,早期JVM版本可能存在DCL失效问题,但在现代JVM中已得到解决。

public class SingletonDCL {
    private volatile static SingletonDCL instance;

    private SingletonDCL() {}

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

5. 静态内部类

优点:利用类加载机制保证线程安全,延迟初始化,无同步开销。

缺点:相比其他实现方式,代码结构稍显复杂。

public class SingletonStaticInnerClass {
    private SingletonStaticInnerClass() {}

    private static class SingletonHolder {
        private static final SingletonStaticInnerClass INSTANCE = new SingletonStaticInnerClass();
    }

    public static SingletonStaticInnerClass getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

分析Spring源码中的单例模式及作用

Spring框架广泛使用了单例模式,尤其是在其核心组件BeanFactory中管理的bean实例。Spring默认将配置文件中定义的bean配置为单例模式,这意味着对于每个指定为单例的bean,Spring容器在整个应用程序生命周期中仅创建一次该bean的实例,并将其缓存起来,后续对该bean的所有请求都将返回相同的实例。

作用:

  1. 资源优化:对于重量级、高消耗资源的对象(如数据库连接、线程池等),通过单例模式限制实例数量,避免资源浪费。
  2. 一致性保证:对于需要保持全局唯一状态或共享状态的bean,单例模式确保所有客户端共享同一份实例,从而维持状态的一致性。
  3. 简化编程模型:开发者无需关心bean实例的创建、管理和销毁,只需关注业务逻辑,降低了代码耦合度。

由于Spring源码庞大且涉及众多模块,这里无法直接给出全部相关源码。但可以简述Spring如何实现单例模式:

Spring的DefaultListableBeanFactory类(或其他实现BeanFactory接口的类)是Spring IoC容器的核心实现,它维护了一个DefaultSingletonBeanRegistry(或类似实现SingletonBeanRegistry接口的类)来管理单例bean。当容器创建一个bean时,首先检查是否已经存在该bean的单例实例。如果存在,直接从缓存中返回;否则,按照bean定义创建新实例,并将其放入缓存中。

以下是一段简化版的伪代码,以说明Spring如何通过SingletonBeanRegistry来实现单例模式:

public interface BeanFactory {
    Object getBean(String beanName);
}

public interface SingletonBeanRegistry {
    Object getSingleton(String beanName);
    void registerSingleton(String beanName, Object singletonObject);
}

public class DefaultListableBeanFactory implements BeanFactory {
    private final SingletonBeanRegistry singletonRegistry;

    public Object getBean(String beanName) {
        return this.singletonRegistry.getSingleton(beanName);
    }
}

public class DefaultSingletonBeanRegistry implements SingletonBeanRegistry {
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>();

    public Object getSingleton(String beanName) {
        return this.singletonObjects.get(beanName);
    }

    public void registerSingleton(String beanName, Object singletonObject) {
        this.singletonObjects.putIfAbsent(beanName, singletonObject);
    }
}

实际Spring源码中,DefaultSingletonBeanRegistry及其相关类还包含了许多额外逻辑,如处理循环依赖、bean的后置处理器、生命周期回调等。以上伪代码仅为了说明Spring如何通过一个注册表(registry)来管理单例bean的创建和缓存,确保整个应用中每个bean名称对应唯一实例。

你可能感兴趣的:(Java基础,Spring源码,设计模式,单例模式,java,spring)