当前位置: 首页 > news >正文

Java 设计模式——单例模式6种写法:从原理到 SpringBoot 落地

Java 设计模式——单例模式6种写法:从原理到 SpringBoot 落地

单例模式是 Java 中最基础却最易踩坑的设计模式 —— 看似简单的 “保证一个实例”,背后藏着线程安全、懒加载、反序列化破坏等诸多细节。本文不仅拆解 6 种单例写法的核心代码,更会通过优缺点对比场景适配SpringBoot 实战案例,帮你彻底搞懂 “哪种写法适合你的项目”,以及 “如何避免单例被破坏”。

文章目录

  • Java 设计模式——单例模式6种写法:从原理到 SpringBoot 落地
    • 一、单例模式核心认知:为什么必须掌握?
    • 二、6 种单例写法深度解析(附代码 + 原理 + 场景)
      • 1. 静态内部类(推荐首选)
        • 核心原理
        • 代码实现
        • 核心特点
        • 适用场景
      • 2. 双重校验锁(DCL,高并发场景首选)
        • 核心原理
        • 代码实现(JDK1.5+)
        • 关键问题解析
        • 核心特点
        • 适用场景
      • 3. 枚举(防破坏场景首选)
        • 核心原理
        • 代码实现(极简)
        • 核心优势:天然防破坏
        • 核心特点
        • 适用场景
      • 4. 饿汉式(简单场景可用)
        • 核心原理
        • 代码实现(两种写法)
        • 核心特点
        • 适用场景
      • 5. 懒汉式(不推荐,仅作对比)
        • 核心原理
        • 代码实现
        • 核心特点
        • 适用场景
      • 6. 非 synchronized 加锁(特殊场景可用)
        • (1)CAS 实现(无锁并发)
        • (2)ThreadLocal 实现(线程内单例)
    • 三、6 种单例写法对比表(实战选型指南)
    • 四、单例模式进阶:如何防止被破坏?
      • 1. 防反射破坏:在私有构造器中添加校验
      • 2. 防反序列化破坏:重写`readResolve()`方法
    • 五、SpringBoot 实战:单例模式的实际应用
      • 1. Spring 的单例 Bean:底层原理与配置
      • 2. 实战场景 1:自定义单例工具类(与 Spring 整合)
      • 3. 实战场景 2:单例 Bean 的线程安全问题(避坑)
    • 六、单例模式常见误区与最佳实践
      • 1. 常见误区(避坑指南)
      • 2. 最佳实践(选型口诀)
    • 七、总结:单例模式的本质是 “平衡”

一、单例模式核心认知:为什么必须掌握?

在开始之前,先明确单例模式的 “不可替代性”—— 它解决的是 “资源独占” 和 “全局访问” 的核心问题:

  • 资源独占场景:工具类(如RedisUtils)、配置类(如AppConfig)、线程池对象,如果重复创建会导致资源浪费或逻辑混乱;

  • 全局访问场景:系统中需要一个统一入口的组件(如日志工厂、缓存管理器),单例模式可避免到处传递实例;

  • 框架底层依赖:Spring 中Bean默认单例、MyBatis 的SqlSessionFactory,本质都是单例模式的应用。

实现单例模式的 3 个核心要素(缺一不可):

  1. 私有构造器:防止外部通过new关键字创建实例;

  2. 静态全局访问点:提供getInstance()等静态方法,供外部获取唯一实例;

  3. 线程安全:多线程环境下,确保实例只被创建一次(线程不安全的单例都是 “伪单例”)。

二、6 种单例写法深度解析(附代码 + 原理 + 场景)

以下按 “实战优先级” 排序,从最常用到特殊场景写法,逐一拆解:

1. 静态内部类(推荐首选)

核心原理

利用 Java 的类加载机制实现线程安全:静态内部类SingletonHolder只有在调用getInstance()时才会被加载,加载时创建外部类实例,且类加载过程由 JVM 保证线程安全。

代码实现
package com.fastech.designpattern.singleton;public class InnerClassSingleton {// 1. 私有构造器:禁止外部newprivate InnerClassSingleton() {// 可选:防止反射破坏(下文会详细讲)if (SingletonHolder.instance != null) {throw new IllegalStateException("单例已被实例化,禁止重复创建");}}// 2. 静态内部类:延迟加载,仅在调用getInstance时加载private static class SingletonHolder {// 类加载时创建实例,JVM保证线程安全private static final InnerClassSingleton instance = new InnerClassSingleton();}// 3. 静态全局访问点public static InnerClassSingleton getInstance() {return SingletonHolder.instance;}
}
核心特点
  • ✅ 线程安全:依赖 JVM 类加载的 “初始化锁”,无需手动加锁;

  • ✅ 完美懒加载:类加载时不创建实例,只有调用getInstance()才初始化;

  • ✅ 高性能:无同步锁开销,获取实例时直接返回,效率极高;

  • ❌ 不防反序列化 / 反射:需额外处理(下文有解决方案)。

适用场景

90% 的业务场景(如工具类、配置类、普通服务组件),尤其是对 “懒加载” 和 “性能” 有要求的场景,是日常开发的首选写法

2. 双重校验锁(DCL,高并发场景首选)

核心原理

“懒汉式” 的升级版 —— 通过 “两次if判断 +volatile禁止指令重排”,既保证线程安全,又减少同步锁的开销(仅首次创建实例时加锁)。

代码实现(JDK1.5+)
package com.fastech.designpattern.singleton;public class DoubleCheckedLockSingleton {// 1. 私有构造器private DoubleCheckedLockSingleton() {}// 2. volatile修饰实例:禁止指令重排+保证可见性(关键!)private static volatile DoubleCheckedLockSingleton instance;// 3. 双重校验锁的全局访问点public static DoubleCheckedLockSingleton getInstance() {// 第一次校验:避免已创建实例后,每次调用都进入同步块(提高性能)if (instance == null) {// 同步锁:保证多线程下只有一个线程进入创建逻辑synchronized (DoubleCheckedLockSingleton.class) {// 第二次校验:防止多个线程等待锁时,重复创建实例(关键!)if (instance == null) {// 若不加volatile,可能发生指令重排(1-3-2),导致其他线程拿到未初始化的实例instance = new DoubleCheckedLockSingleton();}}}return instance;}
}
关键问题解析
  • 为什么加volatile

instance = new DoubleCheckedLockSingleton()在 JVM 中会拆分为 3 步:1. 分配内存空间;2. 初始化实例;3. 将instance指向内存空间。
若不加volatile,JVM 可能进行 “指令重排”(如 1→3→2),导致线程 A 执行到 3 时,instance已非null,线程 B 直接返回未初始化的instance,引发空指针异常。

volatile的作用:禁止指令重排,确保实例初始化完成后,才对外可见。

  • 为什么需要两次if校验?

假设线程 A、B 同时进入第一次ifinstancenull):
线程 A 先获取锁,进入第二次if,创建实例后释放锁;
线程 B 再获取锁,若没有第二次if,会重复创建实例,破坏单例。

核心特点
  • ✅ 线程安全:双重校验 +volatile确保多线程下实例唯一;

  • ✅ 懒加载:仅首次调用时创建实例;

  • ✅ 高性能:首次创建后无锁开销,适合高并发场景;

  • ❌ 代码稍复杂:需注意volatile和双重校验的细节;

  • ❌ 不防反序列化 / 反射:需额外处理。

适用场景

高并发场景(如秒杀系统的库存管理器、分布式锁客户端),或需要 “懒加载 + 低锁开销” 的核心组件。

3. 枚举(防破坏场景首选)

核心原理

利用 Java 枚举的天然特性实现单例:枚举类的实例在类加载时创建,且 JVM 禁止通过反射、反序列化创建新实例,是《Effective Java》作者 Josh Bloch 推荐的 “最安全单例写法”。

代码实现(极简)
package com.fastech.designpattern.singleton.enums;// 枚举单例:3行代码实现最安全的单例
public enum EnumSingleton {// 唯一实例(枚举类的每个枚举值都是单例)INSTANCE;// 可选:添加业务方法public void doBusiness() {System.out.println("枚举单例执行业务逻辑");}
}// 调用方式:直接通过枚举值获取实例
EnumSingleton.INSTANCE.doBusiness();
核心优势:天然防破坏

枚举单例的最大价值,是无需额外代码即可抵御 “反射” 和 “反序列化” 的破坏:

  • 防反射破坏:JVM 在Constructor.newInstance()中明确禁止反射创建枚举实例,若强行调用会抛出IllegalArgumentException
// 测试反射破坏枚举单例(会报错)
public class ReflectTest {public static void main(String[] args) throws Exception {// 枚举类的构造器默认有String(枚举名)和int(序号)两个参数Constructor<EnumSingleton> constructor = EnumSingleton.class.getDeclaredConstructor(String.class, int.class);constructor.setAccessible(true);// 报错:Cannot reflectively create enum objectsEnumSingleton instance = constructor.newInstance("TEST", 1);}
}

具体可以查看newInstance()方法的源码

在这里插入图片描述

  • 防反序列化破坏:枚举类默认实现Serializable,反序列化时会调用Enum.valueOf()方法,根据枚举名获取已存在的实例,而非创建新实例;
// 测试反序列化枚举单例(结果为true,实例唯一)
public class SerializeTest {public static void main(String[] args) throws Exception {// 序列化ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("enum-singleton.txt"));oos.writeObject(EnumSingleton.INSTANCE);// 反序列化ObjectInputStream ois = new ObjectInputStream(new FileInputStream("enum-singleton.txt"));EnumSingleton deserializedInstance = (EnumSingleton) ois.readObject();// 输出true:反序列化后仍是原实例System.out.println(deserializedInstance == EnumSingleton.INSTANCE);}
}
核心特点
  • ✅ 绝对线程安全:枚举实例由 JVM 保证唯一;

  • ✅ 天然防破坏:抵御反射、反序列化攻击;

  • ✅ 代码极简:无需处理锁、volatile 等细节;

  • ❌ 不支持懒加载:枚举类加载时即创建实例(若实例初始化开销大,会影响启动速度);

  • ❌ 灵活性低:枚举类不能继承其他类(已默认继承Enum)。

适用场景

对 “单例安全性” 要求极高的场景(如权限管理器、加密密钥生成器),或需要防止反序列化 / 反射破坏的场景。

4. 饿汉式(简单场景可用)

核心原理

利用 “类加载时初始化实例” 实现单例:类加载的 “准备→初始化” 阶段由 JVM 保证线程安全,实例在类加载时就创建,无需懒加载。

代码实现(两种写法)
// 写法1:静态变量直接初始化
public class HungrySingleton1 {// 类加载时创建实例(线程安全)private static final HungrySingleton1 instance = new HungrySingleton1();private HungrySingleton1() {}public static HungrySingleton1 getInstance() {return instance;}
}// 写法2:静态代码块初始化(与写法1原理一致)
public class HungrySingleton2 {private static final HungrySingleton2 instance;// 静态代码块在类加载时执行,仅执行一次static {instance = new HungrySingleton2();// 可在此处添加实例初始化逻辑(如读取配置)}private HungrySingleton2() {}public static HungrySingleton2 getInstance() {return instance;}
}
核心特点
  • ✅ 线程安全:依赖 JVM 类加载机制,无需加锁;

  • ✅ 高性能:获取实例时直接返回,无任何开销;

  • ✅ 代码简单:易于理解和实现;

  • ❌ 不支持懒加载:类加载时即创建实例,若实例初始化开销大(如加载大文件),会导致系统启动变慢;

  • ❌ 不防反序列化 / 反射:需额外处理。

适用场景

实例初始化开销小、启动时就需要用到的组件(如简单的工具类StringUtils),或小型项目中对 “懒加载” 无要求的场景。

5. 懒汉式(不推荐,仅作对比)

核心原理

“懒加载” 的基础实现:只有调用getInstance()时才创建实例,通过synchronized关键字保证线程安全,但同步锁粒度太大,性能极低。

代码实现
public class LazySingleton {private static LazySingleton instance;private LazySingleton() {}// 整个方法加锁:每次调用都要竞争锁,性能差public static synchronized LazySingleton getInstance() {if (instance == null) {instance = new LazySingleton();}return instance;}
}
核心特点
  • ✅ 线程安全:synchronized保证多线程下实例唯一;

  • ✅ 支持懒加载:仅首次调用时创建实例;

  • ❌ 性能极差:每次调用getInstance()都要加锁,即使实例已创建,仍需竞争锁(对比双重校验锁的 “细粒度锁”,差距明显);

  • ❌ 不防反序列化 / 反射:需额外处理。

适用场景

几乎不推荐在实际项目中使用,仅作为 “单例模式演进” 的学习案例(理解从懒汉式到双重校验锁的优化思路)。

6. 非 synchronized 加锁(特殊场景可用)

这类写法不依赖synchronized,而是通过 CAS 或 ThreadLocal 实现线程安全,属于 “小众但有特定价值” 的写法。

(1)CAS 实现(无锁并发)

核心原理:利用AtomicReferencecompareAndSet()(CAS 操作),在无锁情况下保证实例创建的原子性,适合并发量极高但实例初始化频率低的场景。

package com.fastech.designpattern.singleton;import java.util.concurrent.atomic.AtomicReference;public class CasSingleton {// 原子引用:存储单例实例private static final AtomicReference<CasSingleton> INSTANCE = new AtomicReference<>();private CasSingleton() {}public static CasSingleton getInstance() {while (true) {// 1. 尝试获取已存在的实例CasSingleton instance = INSTANCE.get();if (instance != null) {return instance;}// 2. 若实例不存在,创建新实例并通过CAS尝试设置instance = new CasSingleton();// CAS操作:若当前值为null(无其他线程修改),则设置为新实例,返回trueif (INSTANCE.compareAndSet(null, instance)) {return instance;}// 3. 若CAS失败(其他线程已创建实例),则循环重试}}
}

特点:无锁开销,并发性能高;但循环重试可能消耗 CPU,且不防反射 / 反序列化,适合底层框架或极致性能场景。

(2)ThreadLocal 实现(线程内单例)

核心原理ThreadLocal保证 “每个线程拥有独立实例”,并非 “全局唯一实例”,属于 “线程级单例”,适合线程内需要复用实例但无需全局唯一的场景(如多线程处理任务时,每个线程一个日志器)。

package com.fastech.designpattern.singleton;public class ThreadLocalSingleton {// ThreadLocal:每个线程存储自己的实例private static final ThreadLocal<ThreadLocalSingleton> INSTANCE = ThreadLocal.withInitial(ThreadLocalSingleton::new);private ThreadLocalSingleton() {}// 每个线程调用时,获取自己线程内的实例public static ThreadLocalSingleton getInstance() {return INSTANCE.get();}
}

特点:线程内实例唯一,无并发竞争;但全局不唯一,需明确场景后使用(如 Spring 的RequestContextHolder就是类似思想)。

三、6 种单例写法对比表(实战选型指南)

写法线程安全懒加载防反射 / 反序列化性能代码复杂度适用场景
静态内部类❌(需额外处理)极高日常开发首选(工具类、配置类)
双重校验锁❌(需额外处理)高并发场景(秒杀、分布式锁)
枚举✅(天然支持)极高极低安全优先场景(权限、加密)
饿汉式❌(需额外处理)极高极低简单场景(轻量级工具类)
懒汉式❌(需额外处理)仅学习使用,不推荐项目用
CAS 实现❌(需额外处理)极高极致性能场景(底层框架)
ThreadLocal✅(线程内)❌(需额外处理)极高线程级单例(如线程内日志)

四、单例模式进阶:如何防止被破坏?

除了枚举单例,其他写法都可能被 “反射” 或 “反序列化” 破坏,以下是完整的防护方案:

1. 防反射破坏:在私有构造器中添加校验

核心思路:在私有构造器中判断实例是否已存在,若已存在则抛出异常,阻止反射通过newInstance()重复创建实例。

以 “静态内部类单例” 为例,完善防反射代码:

public class InnerClassSingleton {private InnerClassSingleton() {// 防反射关键代码:若实例已存在,直接抛异常if (SingletonHolder.instance != null) {throw new IllegalStateException("单例对象已存在,禁止通过反射重复创建");}}private static class SingletonHolder {private static final InnerClassSingleton instance = new InnerClassSingleton();}public static InnerClassSingleton getInstance() {return SingletonHolder.instance;}
}

测试反射破坏(此时会报错):

public class ReflectTest {public static void main(String[] args) throws Exception {// 获取私有构造器Constructor<InnerClassSingleton> constructor = InnerClassSingleton.class.getDeclaredConstructor();constructor.setAccessible(true); // 强行访问私有构造器// 第一次创建:正常(此时SingletonHolder.instance为null)InnerClassSingleton instance1 = constructor.newInstance();// 第二次创建:抛出IllegalStateException,阻止重复创建InnerClassSingleton instance2 = constructor.newInstance();}
}

注意:这种方案仅对 “静态内部类”“饿汉式” 等 “实例在类加载时创建” 的写法有效;对 “双重校验锁”“懒汉式” 等 “延迟创建实例” 的写法,需结合volatile和原子变量优化,否则在多线程反射场景下仍有漏洞(实际项目中,反射破坏多为恶意操作,此方案已能应对大部分场景)。

2. 防反序列化破坏:重写readResolve()方法

核心原理:当单例类实现Serializable接口时,反序列化会通过readObject()创建新实例;重写readResolve()方法后,反序列化会直接返回该方法指定的实例(即已存在的单例实例),而非创建新实例。

以 “双重校验锁单例” 为例,完善防反序列化代码:

package com.fastech.designpattern.singleton;import java.io.Serializable;public class DoubleCheckedLockSingleton implements Serializable {private static volatile DoubleCheckedLockSingleton instance;private DoubleCheckedLockSingleton() {}public static DoubleCheckedLockSingleton getInstance() {if (instance == null) {synchronized (DoubleCheckedLockSingleton.class) {if (instance == null) {instance = new DoubleCheckedLockSingleton();}}}return instance;}// 防反序列化关键方法:反序列化时返回已存在的单例实例private Object readResolve() {return getInstance();}
}

测试反序列化破坏(此时结果为true,实例唯一):

public class SerializeTest {public static void main(String[] args) throws Exception {// 1. 序列化单例实例ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("dcl-singleton.txt"));oos.writeObject(DoubleCheckedLockSingleton.getInstance());oos.close();// 2. 反序列化ObjectInputStream ois = new ObjectInputStream(new FileInputStream("dcl-singleton.txt"));DoubleCheckedLockSingleton deserializedInstance = (DoubleCheckedLockSingleton) ois.readObject();ois.close();// 3. 对比:反序列化实例与原实例是否一致(结果为true)System.out.println(deserializedInstance == DoubleCheckedLockSingleton.getInstance());}
}

原理补充readResolve()是 Java 序列化机制的 “钩子方法”,在readObject()之后执行;若类中存在该方法,序列化框架会用其返回值替换readObject()创建的新实例,从而保证单例唯一性。

五、SpringBoot 实战:单例模式的实际应用

在 SpringBoot 项目中,我们很少手动写单例(因为 Spring 默认将Bean创建为单例),但理解单例模式的原理,能帮你更好地解决 “Bean 的作用域”“线程安全” 等核心问题。

1. Spring 的单例 Bean:底层原理与配置

Spring 中Bean默认是 “单例作用域”(singleton),其实现逻辑与 “饿汉式”“静态内部类” 类似,依赖容器启动时创建实例线程安全的 Bean 工厂

  • 创建时机:默认在容器启动(refresh()方法)时创建单例 Bean(可通过@Lazy注解改为 “懒加载”,对应 “静态内部类” 的延迟创建逻辑);

  • 线程安全:Spring 通过DefaultSingletonBeanRegistry中的 “单例缓存” 和 “同步锁” 保证线程安全,核心代码如下(简化版):

public class DefaultSingletonBeanRegistry {// 单例Bean缓存:key=Bean名称,value=Bean实例private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);// 创建Bean的同步锁private final Object singletonMutex = new Object();public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {synchronized (singletonMutex) { // 同步锁保证线程安全Object singletonObject = singletonObjects.get(beanName);if (singletonObject == null) {// 调用ObjectFactory创建Bean实例(如反射创建Bean)singletonObject = singletonFactory.getObject();// 将创建好的Bean放入缓存singletonObjects.put(beanName, singletonObject);}return singletonObject;}}
}

2. 实战场景 1:自定义单例工具类(与 Spring 整合)

在 SpringBoot 中,若需自定义单例工具类(如RedisUtils),推荐结合Spring的依赖注入,避免手动管理单例实例:

// 1. 自定义单例工具类(通过Spring管理,确保单例)
@Component // 交给Spring管理,默认单例
public class RedisUtils {// 依赖注入RedisTemplate(Spring管理的单例Bean)@Autowiredprivate RedisTemplate<String, Object> redisTemplate;// 私有构造器:防止外部new(可选,因为Spring会通过反射创建实例)private RedisUtils() {}// 业务方法:封装Redis操作public void set(String key, Object value, long timeout) {redisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS);}public Object get(String key) {return redisTemplate.opsForValue().get(key);}
}// 2. 其他组件中使用:直接注入,无需手动调用getInstance()
@Service
public class UserService {@Autowiredprivate RedisUtils redisUtils; // 注入的是Spring管理的单例实例public void cacheUser(User user) {redisUtils.set("user:" + user.getId(), user, 3600);}
}

3. 实战场景 2:单例 Bean 的线程安全问题(避坑)

Spring 的单例 Bean 是 “实例唯一”,但不保证线程安全—— 若 Bean 中存在 “可变成员变量”,多线程并发修改时会出现数据混乱。

反例:线程不安全的单例 Bean

@Component
public class ThreadUnsafeSingletonBean {// 可变成员变量:多线程共享会导致线程安全问题private int count = 0;// 多线程并发调用此方法,会出现count计算错误public void increment() {count++;System.out.println("当前计数:" + count);}
}

正例:线程安全的单例 Bean(3 种解决方案)

@Component
public class ThreadSafeSingletonBean {// 方案1:使用ThreadLocal(每个线程有独立变量副本)private ThreadLocal<Integer> count = ThreadLocal.withInitial(() -> 0);// 方案2:使用原子变量(CAS操作保证线程安全)private AtomicInteger atomicCount = new AtomicInteger(0);// 方案3:加同步锁(适合复杂逻辑)private int syncCount = 0;public void increment() {// ThreadLocal方案count.set(count.get() + 1);System.out.println("ThreadLocal计数:" + count.get());// 原子变量方案System.out.println("原子变量计数:" + atomicCount.incrementAndGet());// 同步锁方案synchronized (this) {syncCount++;System.out.println("同步锁计数:" + syncCount);}}
}

六、单例模式常见误区与最佳实践

1. 常见误区(避坑指南)

  • 误区 1:认为 “单例 = 线程安全”

    单例仅保证 “实例唯一”,不保证 “业务逻辑线程安全”(如上述ThreadUnsafeSingletonBean),需额外通过ThreadLocal、原子变量、同步锁保证线程安全。

  • 误区 2:过度依赖单例,导致 “全局状态污染”

    若单例 Bean 中存储过多全局状态(如用户登录信息),会导致多用户共享数据混乱,应使用RequestScope(请求作用域)或SessionScope(会话作用域)的 Bean 替代。

  • 误区 3:手动写单例时忽略 “防破坏”

    若单例类实现Serializable或可能被反射调用,必须添加readResolve()方法和构造器校验,否则会被破坏单例。

2. 最佳实践(选型口诀)

  • 日常开发选 “静态内部类”:兼顾懒加载、线程安全、高性能,适配 90% 的业务场景;

  • 高并发选 “双重校验锁”:细粒度锁减少开销,适合秒杀、分布式锁等核心场景;

  • 安全优先选 “枚举”:天然防反射、反序列化,适合权限、加密等敏感场景;

  • Spring 项目少写 “手动单例”:优先用@Component让 Spring 管理 Bean,利用容器的单例机制和依赖注入简化开发;

  • 线程内复用选 “ThreadLocal”:如多线程处理任务时,每个线程一个日志器、数据库连接。

七、总结:单例模式的本质是 “平衡”

单例模式的核心不是 “写出唯一实例”,而是 “在资源开销、性能、安全性之间找到平衡”:

  • 若追求 “简单高效”,选饿汉式、静态内部类;

  • 若追求 “高并发适配”,选双重校验锁、CAS 实现;

  • 若追求 “绝对安全”,选枚举;

  • 若在 Spring 生态中,优先依赖容器的单例 Bean 机制,避免重复造轮子。

掌握单例模式的 6 种写法,不仅能应对面试中的 “单例实现” 问题,更能理解框架底层的设计逻辑(如 Spring Bean、MyBatis SqlSessionFactory),为后续学习更复杂的设计模式(如工厂模式、代理模式)打下基础。


文章转载自:

http://YSgRC2E8.qyjqj.cn
http://DLYuuw4i.qyjqj.cn
http://Gu2qVlxe.qyjqj.cn
http://u9Z5YKnC.qyjqj.cn
http://mXBLWgUc.qyjqj.cn
http://S2VMK6a6.qyjqj.cn
http://BRtPp6sO.qyjqj.cn
http://JyVXJ4Bp.qyjqj.cn
http://phlAypJW.qyjqj.cn
http://FfHJw1LH.qyjqj.cn
http://cSytZuAd.qyjqj.cn
http://a8br5Vcx.qyjqj.cn
http://F4vHH5qV.qyjqj.cn
http://zSJ4tUrH.qyjqj.cn
http://gO7dCI3e.qyjqj.cn
http://uT4EZjPu.qyjqj.cn
http://hWQaLQYq.qyjqj.cn
http://2KKpWCr6.qyjqj.cn
http://u8ueasBd.qyjqj.cn
http://XJOWlr2e.qyjqj.cn
http://sH2JqvsJ.qyjqj.cn
http://jeBrjk4C.qyjqj.cn
http://XDBytdGm.qyjqj.cn
http://SEZg8JCH.qyjqj.cn
http://NxvS5MmF.qyjqj.cn
http://GHxNm68N.qyjqj.cn
http://NFqK8zvG.qyjqj.cn
http://urUzyofQ.qyjqj.cn
http://tbRST4YR.qyjqj.cn
http://KWJsU0Ai.qyjqj.cn
http://www.dtcms.com/a/384737.html

相关文章:

  • 【自存】懒汉式单例模式中的多线程经典问题
  • 【第五章:计算机视觉-项目实战之图像分类实战】1.经典卷积神经网络模型Backbone与图像-(4)经典卷积神经网络ResNet的架构讲解
  • 区块链:搭建简单以太坊Geth私有链
  • 数据分析:函数
  • 《投资-57》元宇宙的价值
  • Linux任务调度全攻略
  • 基于springboot的毕业旅游一站式定制系统
  • 创建其他服务器账号
  • 前端-详解ref和$refs
  • C++---变量的多维分类
  • Vue 3 前端工程化规范
  • NLP Subword 之 WordPiece 算法原理
  • 【SQL】MySQL中空值处理COALESCE函数
  • Kafka实时数据管道:ETL在流式处理中的应用
  • VBA数据结构深度解析:字典对象与集合对象的性能终极对决
  • 查看当前虚拟环境中安装的 PyTorch 版本
  • 布尔运算-区间dp
  • WWW‘25一通读 |图Anomaly/OOD检测相关文章(1)
  • 视频分类 pytorchvideo
  • RabbitMQ 基础概念与原理
  • 专题:2025中国消费市场趋势与数字化转型研究报告|附360+份报告PDF、数据仪表盘汇总下载
  • 预制菜行业新风向:企业运营与商家协同发展的实践启示
  • 晶台光耦 KL6N137 :以精密光电技术驱动智能开关性能提升
  • 贪心算法应用:最短作业优先(SJF)调度问题详解
  • javaee初阶 文件IO
  • 如何调整滚珠丝杆的反向间隙?
  • Python项目中的包添加后为什么要进行可编辑安装?
  • daily notes[45]
  • 基于51单片机的蓝牙体温计app设计
  • Git版本控制完全指南