Spring 中用到了哪些设计模式?
一则或许对你有用的小广告
欢迎加入小哈的星球,你将获得:专属的实战项目(4个项目都能学) / 1v1 提问 / 简历修改 / Java 学习路线 / 社群讨论 / 学习打卡 / 每月赠书
《Spring AI 项目实战(问答机器人、RAG 智能客服、联网搜索)》已完结,基于
Spring AI + Spring Boot 3.x + JDK 21...,查看介绍《从零手撸:仿小红书(微服务架构)》 已完结,基于
Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...,查看介绍;演示链接:http://116.62.199.48:7070/《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接:http://116.62.199.48/
新开坑项目:《从零手撸:秒杀系统高并发优化实战》 正在更新中...,查看介绍
截止目前,星球内专栏累计输出 150w+ 字,讲解图 5110+ 张,还在持续爆肝中.. 后续还会上新更多项目,已有 4700+ 小伙伴加入学习,欢迎点击围观
面试考察点
-
设计模式认知广度:面试官不仅仅是想听你背出几个模式的名字,更是想知道你能否将设计模式与 Spring 的具体实现对应起来,而不是泛泛而谈。
-
源码阅读深度:能准确说出每个设计模式在 Spring 中的对应组件,说明你确实读过源码,而不只是看过八股文。面试官特别在意这点。
-
理论联系实际:考察你是否理解为什么要用这些设计模式,解决了什么问题,以及在你自己的项目中能否借鉴运用。
核心答案
Spring 框架中用到的设计模式非常多,面试中最常被考察的有 9 大设计模式:
| 设计模式 | Spring 中的典型应用 | 一句话说明 |
|---|---|---|
| 工厂模式 | BeanFactory、ApplicationContext |
把对象的创建过程封装起来 |
| 单例模式 | Bean 的默认作用域 | 整个容器中只有一个实例 |
| 代理模式 | AOP(JDK 动态代理 / CGLIB) | 不改源码就能增强功能 |
| 模板方法模式 | JdbcTemplate、RestTemplate |
固定骨架,可变步骤留给子类 |
| 观察者模式 | 事件机制 ApplicationEvent |
一处发布,多处监听 |
| 适配器模式 | HandlerAdapter、MessageConverter |
让不兼容的接口能协同工作 |
| 策略模式 | Resource 接口、InstantiationStrategy |
同一接口,不同实现策略 |
| 责任链模式 | Filter 链、Interceptor 链 |
请求沿着链条依次处理 |
| 装饰器模式 | BeanWrapper、HttpRequestDecorator |
动态增强对象功能 |
下面挑最核心的几个展开讲。
深度解析
一、工厂模式(Spring 的心脏)
工厂模式是 Spring 的基石。你天天写 @Component、@Service,有没有想过这些对象是谁创建的?就是 BeanFactory。
上图展示了 Spring 工厂体系的层次结构:
BeanFactory:最顶层的工厂接口,提供了getBean()这样的基本能力,属于 "懒加载",用到 Bean 的时候才创建。ApplicationContext:BeanFactory的超集,除了创建 Bean,还集成了国际化、事件发布、资源加载等功能。我们平时用的@SpringBootApplication启动后拿到的就是它。- 具体实现有
ClassPathXmlApplicationContext(XML 配置时代)和AnnotationConfigApplicationContext(注解时代),不同的工厂实现从不同的配置源读取 Bean 定义,但对外暴露的getBean()接口完全一致。
这就是工厂模式的核心思想:把对象的创建逻辑封装在工厂内部,调用方不需要关心对象是怎么 new 出来的。
二、单例模式(Spring Bean 的默认作用域)
Spring 的单例不是传统意义上的 "写个 private 构造器 + static 实例" 那种单例,而是 容器级别的单例——由容器来保证同一个 id 的 Bean 只创建一次。
// Spring 的单例实现(DefaultSingletonBeanRegistry 源码简化)
public class DefaultSingletonBeanRegistry {
// 一级缓存:存放完全初始化好的单例 Bean
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>();
public Object getSingleton(String beanName) {
Object singleton = singletonObjects.get(beanName);
if (singleton == null) {
singleton = createBean(beanName);
singletonObjects.put(beanName, singleton);
}
return singleton;
}
}
Spring 用 三级缓存 来解决单例 Bean 的循环依赖问题,这个在面试中经常和单例模式一起追问。有兴趣的可以去看 DefaultSingletonBeanRegistry 的源码,三个 Map 摆在那里,一目了然。
补充一句:Spring 的单例是非线程安全的。因为单例 Bean 在容器中只有一个实例,如果 Bean 里有可变状态,多线程并发访问就会出问题。所以 Spring 官方推荐 Bean 设计为 无状态 的。
三、代理模式(AOP 的灵魂)
这个应该是大家最熟悉的。Spring AOP 的两种实现方式——JDK 动态代理和 CGLIB 代理,本质上都是代理模式的应用。
// JDK 动态代理示例(Spring AOP 的原理简化)
public class JdkProxyDemo implements InvocationHandler {
private Object target;
public Object createProxy(Object target) {
this.target = target;
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this
);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 前置通知
System.out.println("Before: " + method.getName());
// 执行目标方法
Object result = method.invoke(target, args);
// 后置通知
System.out.println("After: " + method.getName());
return result;
}
}
代理模式的精髓在于:不修改原有代码的前提下,对功能进行增强。不管是日志、事务、权限控制,统统可以通过代理来织入,这就是 AOP 能成立的基础。
四、模板方法模式(JdbcTemplate 一族)
模板方法模式在 Spring 中用得非常多,但凡名字里带 Template 的,基本都是。
// JdbcTemplate 的典型用法
jdbcTemplate.query("SELECT * FROM user WHERE id = ?",
(rs, rowNum) -> { // ← 你只需要关注这一部分
User user = new User();
user.setId(rs.getLong("id"));
user.setName(rs.getString("name"));
return user;
},
1L // 参数
);
// 连接获取、Statement 创建、异常处理、资源关闭 —— 全部由模板搞定
模板方法模式的套路是:父类定义算法骨架(固定流程),子类实现可变步骤。JdbcTemplate 把 JDBC 那套繁琐的 try-catch-finally 流程全部封装好了,你只需要告诉它 "SQL 是什么" 和 "结果怎么映射",其他的一概不用操心。
Spring 中类似的还有 RestTemplate、JmsTemplate、RedisTemplate,套路完全一样。
五、观察者模式(事件机制)
Spring 的事件机制就是标准的观察者模式:一个事件发布者,N 个事件监听者。
// 1. 定义事件
public class OrderCreatedEvent extends ApplicationEvent {
private Order order;
public OrderCreatedEvent(Object source, Order order) {
super(source);
this.order = order;
}
}
// 2. 发布事件
@Service
public class OrderService {
@Autowired
private ApplicationEventPublisher publisher;
public void createOrder(Order order) {
// 业务逻辑...
publisher.publishEvent(new OrderCreatedEvent(this, order));
}
}
// 3. 监听事件(可以有多个监听者)
@Component
public class EmailNotifier {
@EventListener
public void onOrderCreated(OrderCreatedEvent event) {
// 发邮件通知
}
}
@Component
public class PointsService {
@EventListener
public void onOrderCreated(OrderCreatedEvent event) {
// 积分奖励
}
}
这个模式在实际项目中非常有用。下单之后要发短信、送积分、更新库存……如果全写在一个方法里,耦合度爆炸。用事件机制解耦之后,各干各的,互不影响,新增功能只需要加个 @EventListener 就行。
六、适配器模式(Spring MVC 的核心)
Spring MVC 中 HandlerAdapter 就是典型的适配器模式。
上图的逻辑是:
DispatcherServlet拿到一个 Handler 后,不知道它具体是什么类型(可能是传统Controller接口,可能是@RequestMapping注解方法,也可能是HttpRequestHandler)。- 于是
DispatcherServlet不直接调用 Handler,而是找到对应的HandlerAdapter,由适配器来负责调用。每种 Handler 都有自己专属的适配器实现。 - 这样
DispatcherServlet只需要面向HandlerAdapter这个统一接口编程,完全不用关心底层 Handler 的差异。
这就是适配器模式的价值:让原本不兼容的接口能够协同工作。
七、策略模式
Spring 中策略模式的应用也很多,比如资源加载:
// Resource 接口有多种实现策略
Resource resource1 = new ClassPathResource("config.xml"); // 类路径
Resource resource2 = new FileSystemResource("/etc/config.xml"); // 文件系统
Resource resource3 = new UrlResource("https://example.com/config.xml"); // 网络
// AbstractApplicationContext 中的资源加载策略
public interface ResourceLoader {
Resource getResource(String location);
}
调用方只面向 Resource 接口编程,至于底层是从类路径读、从磁盘读、还是从网络读,由具体的策略实现来决定。
面试高频追问
-
Spring 的单例是线程安全的吗?
- 不是。Spring 只保证容器中只有一个实例,但不保证线程安全。Bean 中如果包含可变状态(比如成员变量),需要自己处理并发问题。所以 Spring 官方推荐 Bean 设计为无状态的。
-
Spring 的三级缓存是怎么回事?
singletonObjects(一级)、earlySingletonObjects(二级)、singletonFactories(三级)。三级缓存主要为了解决循环依赖场景下的代理对象创建问题。不是所有循环依赖都能解决——构造器注入的循环依赖就搞不定。
-
工厂模式和简单工厂有什么区别?
- 简单工厂通过参数决定创建哪个产品,违反了开闭原则。Spring 用的是工厂方法模式(每个 BeanDefinition 对应一个创建逻辑)和抽象工厂模式(
BeanFactory接口有多种实现),扩展性更好。
- 简单工厂通过参数决定创建哪个产品,违反了开闭原则。Spring 用的是工厂方法模式(每个 BeanDefinition 对应一个创建逻辑)和抽象工厂模式(
常见面试变体
- "说说 Spring MVC 中用到了哪些设计模式?"
- "Spring 的 AOP 用了什么设计模式?具体怎么实现的?"
- "为什么要用工厂模式?直接 new 不行吗?"
- "你在项目中有用到过设计模式吗?"
记忆口诀
"工单代模观,适策责装"——工厂、单例、代理、模板方法、观察者、适配器、策略、责任链、装饰器。9 大模式一口吞。
总结
Spring 框架几乎把 GoF 23 种设计模式用了个遍。面试中重点掌握工厂模式(BeanFactory)、单例模式(Bean 作用域)、代理模式(AOP)、模板方法模式(JdbcTemplate)这四个,再能讲清楚适配器模式(HandlerAdapter)和观察者模式(事件机制),就足够让面试官满意了。关键是每个模式都要能对应到 Spring 的具体实现组件,而不是干巴巴地背概念。
