Spring 中用到了哪些设计模式?

一则或许对你有用的小广告

欢迎 加入小哈的星球 ,你将获得: 专属的项目实战(已更新的所有项目都能学习) / 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/

面试考察点

面试官提出这个问题,通常旨在考察以下几个层面:

  1. 对 Spring 框架核心思想的理解深度:Spring 的核心是 IoC(控制反转)AOP(面向切面编程),面试官想知道你是否能识别出支撑这些核心思想的具体设计模式。
  2. 设计模式的应用与关联能力:不仅仅是对设计模式定义的死记硬背,更是考察你是否能在复杂的框架源码和日常使用中,识别出模式的具体应用场景及其解决的问题。
  3. 知识广度与系统性:Spring 是一个庞大而精密的框架,了解其内部使用的多种设计模式,能反映你对框架整体的认知是否全面、系统。
  4. 实际开发经验与最佳实践:通过你对这些模式在 Spring 中应用的描述,可以判断你是否理解其设计初衷,以及能否在自己的代码中借鉴类似思想。

核心答案

Spring 框架在其内部实现中大量、精妙地运用了多种设计模式,这些模式是 Spring 实现其 “轻量级”、“非侵入式”、“高可扩展性”等特性的基石。其中最核心和常见的设计模式包括:

  • 工厂模式:核心容器 BeanFactoryApplicationContext
  • 单例模式:Spring 容器中 Bean 的默认作用域。
  • 代理模式:实现 AOP 功能、@Transactional 事务管理的基石。
  • 模板方法模式JdbcTemplateRestTemplateTransactionTemplate*Template 类。
  • 观察者模式:基于 ApplicationEventApplicationListener 的事件驱动模型。
  • 适配器模式:Spring MVC 中的 HandlerAdapter,用于适配多种处理器。
  • 装饰器模式BeanDefinitionDecorator、在 Wrapper 类中的应用。
  • 策略模式:资源加载(ResourceLoader)、数据访问(PlatformTransactionManager)等场景。

技术深度解析

Spring 不仅是 “用了” 这些模式,更是将它们与自身核心理念深度绑定。我们来深入解析几个最关键的。

原理/机制

  1. 工厂模式 (核心:IoC 容器)

    • 原理:Spring 容器 (BeanFactory 及其子接口 ApplicationContext) 本质上是一个超级工厂。它负责根据配置(XML、注解)或代码创建、组装和管理对象的生命周期。开发者不再直接 new 对象,而是向这个工厂 “索取”(依赖查找)或由工厂 “注入”(依赖注入),实现了控制权的反转。
    • 解决的问题:将对象的创建与使用解耦,提高了系统的灵活性和可维护性。
  2. 单例模式 (核心:Bean 作用域)

    • 原理:Spring 管理的 Bean 默认是单例的。容器会确保在整个应用上下文中,对同一个 Bean ID 的请求都返回同一个对象实例。这并非通过类的静态方法实现,而是由 IoC 容器自身来管理和缓存 Bean 实例。
    • 解决的问题:避免了频繁创建和销毁对象带来的性能开销,特别适合无状态的公共服务类(如 DAO、Service)。注意:这不同于 GoF 的单例模式,Spring 的单例是容器级别的。
  3. 代理模式 (核心:AOP 与声明式事务)

    • 原理:Spring AOP 默认使用 JDK 动态代理(针对接口)或 CGLIB 字节码增强(针对类)来创建目标对象的代理。当调用目标方法时,调用会被代理对象拦截,从而可以在方法执行前后插入增强逻辑(如日志、事务)。
    • 解决的问题:实现了横切关注点(如事务、安全、日志)与核心业务逻辑的分离,是面向切面编程(AOP)的底层实现机制。@Transactional 注解生效的秘密就在于此。
  4. 模板方法模式 (核心:消除样板代码)

    • 原理:在父类(抽象类)中定义一个算法的骨架,并将一些步骤延迟到子类中实现。Spring 的 JdbcTemplate 是一个典范:它封装了获取连接、创建语句、执行SQL、处理异常、关闭连接等固定流程,而开发者只需要通过回调接口(如 RowMapper)提供 “如何映射结果集” 这个可变部分。
    • 解决的问题:极大消除了重复的样板代码(如 JDBC 繁琐的资源管理),让开发者专注于核心逻辑,提高了代码的整洁性和可维护性。

代码示例

以下是通过注解方式体现工厂和单例模式的简单示例:

// 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 框架精髓的关键一步。