Spring 中用到了哪些设计模式?
2026年02月02日
一则或许对你有用的小广告
欢迎 加入小哈的星球 ,你将获得: 专属的项目实战(已更新的所有项目都能学习) / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论
- 新开坑项目: 《Spring AI 项目实战(问答机器人、RAG 增强检索、联网搜索)》 正在持续爆肝中,基于
Spring AI + Spring Boot3.x + JDK 21..., 点击查看; - 《从零手撸:仿小红书(微服务架构)》 已完结,基于
Spring Cloud Alibaba + Spring Boot3.x + JDK 17..., 点击查看项目介绍; 演示链接: http://116.62.199.48:7070/; - 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/
面试考察点
面试官提出这个问题,通常旨在考察以下几个层面:
- 对 Spring 框架核心思想的理解深度:Spring 的核心是 IoC(控制反转) 和 AOP(面向切面编程),面试官想知道你是否能识别出支撑这些核心思想的具体设计模式。
- 设计模式的应用与关联能力:不仅仅是对设计模式定义的死记硬背,更是考察你是否能在复杂的框架源码和日常使用中,识别出模式的具体应用场景及其解决的问题。
- 知识广度与系统性:Spring 是一个庞大而精密的框架,了解其内部使用的多种设计模式,能反映你对框架整体的认知是否全面、系统。
- 实际开发经验与最佳实践:通过你对这些模式在 Spring 中应用的描述,可以判断你是否理解其设计初衷,以及能否在自己的代码中借鉴类似思想。
核心答案
Spring 框架在其内部实现中大量、精妙地运用了多种设计模式,这些模式是 Spring 实现其 “轻量级”、“非侵入式”、“高可扩展性”等特性的基石。其中最核心和常见的设计模式包括:
- 工厂模式:核心容器
BeanFactory、ApplicationContext。 - 单例模式:Spring 容器中 Bean 的默认作用域。
- 代理模式:实现 AOP 功能、
@Transactional事务管理的基石。 - 模板方法模式:
JdbcTemplate、RestTemplate、TransactionTemplate等*Template类。 - 观察者模式:基于
ApplicationEvent和ApplicationListener的事件驱动模型。 - 适配器模式:Spring MVC 中的
HandlerAdapter,用于适配多种处理器。 - 装饰器模式:
BeanDefinitionDecorator、在 Wrapper 类中的应用。 - 策略模式:资源加载(
ResourceLoader)、数据访问(PlatformTransactionManager)等场景。
技术深度解析
Spring 不仅是 “用了” 这些模式,更是将它们与自身核心理念深度绑定。我们来深入解析几个最关键的。
原理/机制
-
工厂模式 (核心:IoC 容器):
- 原理:Spring 容器 (
BeanFactory及其子接口ApplicationContext) 本质上是一个超级工厂。它负责根据配置(XML、注解)或代码创建、组装和管理对象的生命周期。开发者不再直接new对象,而是向这个工厂 “索取”(依赖查找)或由工厂 “注入”(依赖注入),实现了控制权的反转。 - 解决的问题:将对象的创建与使用解耦,提高了系统的灵活性和可维护性。
- 原理:Spring 容器 (
-
单例模式 (核心:Bean 作用域):
- 原理:Spring 管理的 Bean 默认是单例的。容器会确保在整个应用上下文中,对同一个 Bean ID 的请求都返回同一个对象实例。这并非通过类的静态方法实现,而是由 IoC 容器自身来管理和缓存 Bean 实例。
- 解决的问题:避免了频繁创建和销毁对象带来的性能开销,特别适合无状态的公共服务类(如 DAO、Service)。注意:这不同于 GoF 的单例模式,Spring 的单例是容器级别的。
-
代理模式 (核心:AOP 与声明式事务):
- 原理:Spring AOP 默认使用 JDK 动态代理(针对接口)或 CGLIB 字节码增强(针对类)来创建目标对象的代理。当调用目标方法时,调用会被代理对象拦截,从而可以在方法执行前后插入增强逻辑(如日志、事务)。
- 解决的问题:实现了横切关注点(如事务、安全、日志)与核心业务逻辑的分离,是面向切面编程(AOP)的底层实现机制。
@Transactional注解生效的秘密就在于此。
-
模板方法模式 (核心:消除样板代码):
- 原理:在父类(抽象类)中定义一个算法的骨架,并将一些步骤延迟到子类中实现。Spring 的
JdbcTemplate是一个典范:它封装了获取连接、创建语句、执行SQL、处理异常、关闭连接等固定流程,而开发者只需要通过回调接口(如RowMapper)提供 “如何映射结果集” 这个可变部分。 - 解决的问题:极大消除了重复的样板代码(如 JDBC 繁琐的资源管理),让开发者专注于核心逻辑,提高了代码的整洁性和可维护性。
- 原理:在父类(抽象类)中定义一个算法的骨架,并将一些步骤延迟到子类中实现。Spring 的
代码示例
以下是通过注解方式体现工厂和单例模式的简单示例:
// 1. 通过 @Component 注解将一个类声明为由 Spring 工厂管理的 Bean
@Component // 等价于在 XML 中配置 <bean id="userService" class="...">
public class UserServiceImpl implements UserService {
// ...业务逻辑
}
// 2. 在另一个 Bean 中,通过 @Autowired 由 Spring 工厂完成依赖注入(单例实例)
@Service
public class OrderService {
@Autowired // Spring 容器会自动注入唯一的 UserServiceImpl 实例
private UserService userService;
public void createOrder() {
userService.doSomething(); // 使用的永远是容器中的那个单例
}
}
最佳实践
- 理解而非滥用:Spring 已经为我们集成了这些模式,应优先使用 Spring 提供的抽象(如
JdbcTemplate),而不是自己重新实现。 - 关注 Bean 的作用域:明确业务需求,正确使用
@Scope注解(如prototype,request,session),避免在需要多例的地方误用单例导致线程安全问题。 - AOP 的应用:善用 Spring AOP 来处理日志、监控、缓存、重试等通用横切逻辑,保持业务代码的纯净。
常见误区
- “Spring 的单例 Bean 是线程安全的吗?”:不一定! Spring 只保证实例唯一,不保证其内部状态的安全。如果 Bean 有可变的成员变量,需要开发者自己保证线程安全。
- “为什么我的
@Transactional不生效?”:通常是因为代理机制失效。例如,在同一个类中非@Transactional方法 A 调用@Transactional方法 B,由于调用没有经过代理对象,事务注解不会生效。
总结
Spring 框架通过大量、恰当地运用工厂、单例、代理、模板方法等设计模式,优雅地实现了 IoC、AOP 等核心思想,成功地将复杂性封装在框架内部,为开发者提供了一个简洁、强大、可扩展的企业级开发平台。理解这些模式在 Spring 中的应用,是深入掌握 Spring 框架精髓的关键一步。