【Spring6笔记】 - 15 - Spring中的八大设计模式

【Spring6笔记】 - 15 - Spring中的八大设计模式

1. 简单工厂模式 (Simple Factory Pattern) ------ 容器的诞生

1.1 核心定义与 Spring 的选择

简单工厂模式通过一个中心工厂类,根据传入的参数动态决定创建哪一种产品类的实例。在 Spring 中,这个"中心工厂"就是 BeanFactory

1.2 源码级深度剖析:getBean 的背后

当我们在 Spring 6 中调用 context.getBean("userService") 时,内部触发了复杂的工厂逻辑:

  1. BeanDefinition 注册表 :Spring 并不是直接硬编码对象,而是先将 XML 或注解配置解析为 BeanDefinition

  2. AbstractBeanFactory 的生产线 : 在 org.springframework.beans.factory.support.AbstractBeanFactory 中,核心方法是 doGetBean

    java 复制代码
    // 源码逻辑简述
    protected <T> T doGetBean(String name, ...) {
        // 1. 转换 BeanName(处理别名和 FactoryBean 前缀 &)
        String beanName = transformedBeanName(name);
    
        // 2. 检查单例池(简单工厂的缓存机制)
        Object sharedInstance = getSingleton(beanName);
    
        // 3. 如果缓存没有,开始创建
        final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
        if (mbd.isSingleton()) {
            sharedInstance = getSingleton(beanName, () -> {
                return createBean(beanName, mbd, args);
            });
        }
        // ... 返回实例
    }

1.3 模式演变:从静态到动态

虽然经典简单工厂常使用 static 方法,但 Spring 的 BeanFactory 是一个接口实例。这种设计避免了静态方法无法继承和扩展的弊端,体现了 Spring 对模式的改良


2. 工厂方法模式 (Factory Method Pattern) ------ 高级扩展接口

2.1 模式定义

工厂方法模式将实际创建对象的工作推迟到子类中。在 Spring 中,FactoryBean<T> 是该模式的灵魂。

2.2 FactoryBean 的精妙之处

普通 Bean 由 Spring 工厂直接反射创建,而 FactoryBean 允许开发者自定义逻辑。

  • 接口定义

    java 复制代码
    public interface FactoryBean<T> {
        T getObject() throws Exception; // 真正的工厂方法
        Class<?> getObjectType();
        default boolean isSingleton() { return true; }
    }
  • 源码应用场景 : 当集成 MyBatis 时,SqlSessionFactoryBean 实现了这个接口。Spring 在容器启动时会发现它是一个工厂 Bean,于是调用 getObject() 来获取 MyBatis 的核心工厂对象,而不是将 SqlSessionFactoryBean 本身注入给业务类。


3. 单例模式 (Singleton Pattern) ------ 极致的并发性能

3.1 为什么 Spring 不用传统的单例写法?

传统的单例(如饿汉/懒汉)是进程级别的强限制,而 Spring 的单例是 "容器级别(IOC Container)" 的单例。这意味着在一个 JVM 中,如果有两个 ApplicationContext,同一个 Bean ID 可以存在两个实例。

3.2 深度源码:getSingleton 与三级缓存

您提到的 getSingleton 方法位于 DefaultSingletonBeanRegistry 类中。Spring 解决循环依赖和保证线程安全的核心就在于此。

三级缓存结构:
  1. singletonObjects (一级缓存):存储完全初始化好的 Bean。
  2. earlySingletonObjects (二级缓存):存储提前暴露的、尚未完成填充属性的 Bean。
  3. singletonFactories (三级缓存):存储 ObjectFactory,用于处理 AOP 代理。
双重检查锁 (DCL) 源码还原:
java 复制代码
public Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 第一次检查:从一级缓存获取
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        // 加锁,保证原子性
        synchronized (this.singletonObjects) {
            // 第二次检查:防止在加锁期间其他线程已经创建好了
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    singletonObject = singletonFactory.getObject();
                    // 提升到二级缓存
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}

4. 代理模式 (Proxy Pattern) ------ AOP 的基石

4.1 静态代理 vs 动态代理

Spring AOP 抛弃了维护成本极高的静态代理,全面拥抱动态代理。

4.2 JDK 与 CGLIB 的策略选择

Spring 自动在两者间切换:

  • JDK Proxy :要求目标类必须实现接口。原理是利用 Proxy.newProxyInstance 生成一个实现相同接口的匿名类。
  • CGLIB:通过字节码增强技术,生成目标类的子类。如果目标类没有接口,Spring 会强制使用 CGLIB。

4.3 核心类:ProxyFactory

在 Spring 6 源码中,ProxyFactory 是代理模式的汇聚点。它根据配置决定调用 JdkDynamicAopProxy 还是 CglibAopProxy


5. 装饰器模式 (Decorator Pattern) ------ 灵活的功能包装

5.1 模式本质

装饰器模式(Wrapper)在不改变原类结构的情况下,动态地给对象增加职责。

5.2 Spring 中的 DataSources 切换

在大型系统中,我们需要实现"动态数据源"。

  • AbstractRoutingDataSource:它装饰了多个 DataSource。根据当前的 LookupKey(比如 ThreadLocal 里的租户 ID),决定具体调用哪一个真实的数据库连接。
  • 命名习惯 :在 Spring 源码中,看到 BeanWrapperHttpHeadResponseDecorator 等,都是装饰器模式的体现。

6. 观察者模式 (Observer Pattern) ------ 事件驱动的解耦

6.1 事件驱动模型 (Spring Event)

观察者模式允许容器在状态变化时通知其他组件,而不需要组件之间显式依赖。

6.2 源码级流程

  1. 事件 (ApplicationEvent) :继承自 EventObject

  2. 发布者 (ApplicationEventPublisher) :通常是 ApplicationContext

  3. 广播器 (ApplicationEventMulticaster):这是核心。

    java 复制代码
    // 源码:SimpleApplicationEventMulticaster.multicastEvent
    for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
        // 如果有线程池,可以实现异步观察者模式
        Executor executor = getTaskExecutor();
        if (executor != null) {
            executor.execute(() -> invokeListener(listener, event));
        } else {
            invokeListener(listener, event);
        }
    }

7. 策略模式 (Strategy Pattern) ------ 消除 If-Else 的利器

7.1 Spring MVC 中的策略应用

HandlerMapping 接口就是典型的策略接口。

  • BeanNameUrlHandlerMapping:基于 Bean 名称寻找 Controller。
  • RequestMappingHandlerMapping :基于 @RequestMapping 注解寻找。

7.2 实例化策略

Spring 在创建对象时,并不是简单的 new。它定义了 InstantiationStrategy 接口,默认实现类是 CglibSubclassingInstantiationStrategy。这允许 Spring 根据是否有配置 lookup-method 动态选择是用反射还是用 CGLIB 生成子类。


8. 模板方法模式 (Template Method Pattern) ------ 流程标准化的顶层设计

8.1 什么是 Spring 的"模板"?

Spring 将那些"开启资源 -> 执行业务 -> 关闭资源 -> 异常处理"的繁琐代码封装在父类(模板)中,将"执行业务"这个变数留给用户。

8.2 JdbcTemplate 的标准化流程

java 复制代码
// 伪代码展示模板结构
public <T> T execute(StatementCallback<T> action) {
    Connection con = DataSourceUtils.getConnection(getDataSource()); // 1. 固定的获取连接
    try {
        Statement stmt = con.createStatement();
        T result = action.doInStatement(stmt); // 2. 变化的行为(钩子方法/回调)
        return result;
    } catch (SQLException ex) {
        throw translateException(ex); // 3. 固定的异常转换
    } finally {
        JdbcUtils.closeStatement(stmt); // 4. 固定的释放资源
    }
}

8.3 容器启动的模板:refresh()

AbstractApplicationContext.refresh() 是 Spring 最大的模板方法。它规定了容器启动的 12 个步骤。子类可以通过重写 onRefresh()(如 Web 容器创建 Web 服务器)来插入特定逻辑,而不必改动启动主干。


总结:模式的组合拳

在 Spring 6 中,这些模式往往是组合使用的:

  • 通过简单工厂获取 Bean;
  • 如果是单例模式,则进入双重检查锁逻辑;
  • 如果配置了 AOP,则通过代理模式 返回一个装饰过的代理对象;
  • 在执行数据库操作时,调用 JdbcTemplate 这个模板方法
  • 最后通过观察者模式发布容器启动成功的事件。
相关推荐
Engineer邓祥浩2 小时前
JVM学习笔记(8) 第三部分 虚拟机执行子系统 第7章 虚拟机类加载机制
jvm·笔记·学习
深蓝海拓2 小时前
基于QtPy (PySide6) 的PLC-HMI工程项目(七)上位机通信部分的初步建设:socket客户端
网络·笔记·python·学习·plc
蚰蜒螟2 小时前
深入剖析 Tomcat 9.0.53 源码:Web 资源管理与类加载机制
java·前端·tomcat
绛橘色的日落(。・∀・)ノ2 小时前
机器学习笔记
笔记
m0_475064502 小时前
Spring AI文档切片
java·人工智能·spring
我登哥MVP2 小时前
【SpringMVC笔记】 - 1 - SpringMVC入门
java·spring boot·spring·tomcat·maven·intellij-idea·springmvc
Arva .2 小时前
Spring 事务传播机制 速记
java·数据库·spring
0xDevNull2 小时前
Spring Boot 2.0动态多数据源切换实战教程
java·后端
语戚2 小时前
力扣 2463. 最小移动总距离 —— 动态规划 & 贪心排序全解(Java 实现)
java·算法·leetcode·贪心算法·动态规划·力扣·dp