【Spring6笔记】 - 15 - Spring中的八大设计模式
1. 简单工厂模式 (Simple Factory Pattern) ------ 容器的诞生
1.1 核心定义与 Spring 的选择
简单工厂模式通过一个中心工厂类,根据传入的参数动态决定创建哪一种产品类的实例。在 Spring 中,这个"中心工厂"就是 BeanFactory。
1.2 源码级深度剖析:getBean 的背后
当我们在 Spring 6 中调用 context.getBean("userService") 时,内部触发了复杂的工厂逻辑:
-
BeanDefinition 注册表 :Spring 并不是直接硬编码对象,而是先将 XML 或注解配置解析为
BeanDefinition。 -
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 允许开发者自定义逻辑。
-
接口定义:
javapublic 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 解决循环依赖和保证线程安全的核心就在于此。
三级缓存结构:
- singletonObjects (一级缓存):存储完全初始化好的 Bean。
- earlySingletonObjects (二级缓存):存储提前暴露的、尚未完成填充属性的 Bean。
- 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 源码中,看到
BeanWrapper、HttpHeadResponseDecorator等,都是装饰器模式的体现。
6. 观察者模式 (Observer Pattern) ------ 事件驱动的解耦
6.1 事件驱动模型 (Spring Event)
观察者模式允许容器在状态变化时通知其他组件,而不需要组件之间显式依赖。
6.2 源码级流程
-
事件 (ApplicationEvent) :继承自
EventObject。 -
发布者 (ApplicationEventPublisher) :通常是
ApplicationContext。 -
广播器 (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这个模板方法; - 最后通过观察者模式发布容器启动成功的事件。