Spring中用到的设计模式

文章目录

  • [1 Spring中设计模式](#1 Spring中设计模式)
    • [1.1 概述](#1.1 概述)
    • [1.2 深度解析](#1.2 深度解析)
      • [1.2.1 工厂模式(Spring 心脏)](#1.2.1 工厂模式(Spring 心脏))
      • [1.2.2 单例模式(Spring Bean 默认作用域)](#1.2.2 单例模式(Spring Bean 默认作用域))
      • [1.2.3 代理模式(AOP 灵魂)](#1.2.3 代理模式(AOP 灵魂))
      • [1.2.4 模板方法模式(JdbcTemplate 一族)](#1.2.4 模板方法模式(JdbcTemplate 一族))
      • [1.2.5 观察者模式(事件机制)](#1.2.5 观察者模式(事件机制))
      • [1.2.6 适配器模式(Spring MVC 的核心)](#1.2.6 适配器模式(Spring MVC 的核心))
      • [1.2.7 策略模式](#1.2.7 策略模式)

1 Spring中设计模式

1.1 概述

Spring 框架中用到的设计模式非常多,面试中最常被考察的有 9 大设计模式:

设计模式 Spring 中的典型应用 一句话说明
工厂模式 BeanFactoryApplicationContext 把对象的创建过程封装起来
单例模式 Bean 的默认作用域 整个容器中只有一个实例
代理模式 AOP(JDK 动态代理 / CGLIB) 不改源码就能增强功能
模板方法模式 JdbcTemplateRestTemplate 固定骨架,可变步骤留给子类
观察者模式 事件机制 ApplicationEvent 一处发布,多处监听
适配器模式 HandlerAdapterMessageConverter 让不兼容的接口能协同工作
策略模式 Resource 接口、InstantiationStrategy 同一接口,不同实现策略
责任链模式 Filter 链、Interceptor 请求沿着链条依次处理
装饰器模式 BeanWrapperHttpRequestDecorator 动态增强对象功能

1.2 深度解析

1.2.1 工厂模式(Spring 心脏)

工厂模式是 Spring 的基石。你天天写 @Component、@Service,有没有想过这些对象是谁创建的?就是 BeanFactory。

上图展示了 Spring 工厂体系的层次结构:

  • BeanFactory:最顶层的工厂接口,提供了 getBean() 这样的基本能力,属于 "懒加载",用到 Bean 的时候才创建。
  • ApplicationContextBeanFactory 的超集,除了创建 Bean,还集成了国际化、事件发布、资源加载等功能。我们平时用的 @SpringBootApplication 启动后拿到的就是它。
  • 具体实现有 ClassPathXmlApplicationContext(XML 配置时代)和 AnnotationConfigApplicationContext(注解时代),不同的工厂实现从不同的配置源读取 Bean 定义,但对外暴露的 getBean() 接口完全一致。

这就是工厂模式的核心思想:把对象的创建逻辑封装在工厂内部,调用方不需要关心对象是怎么 new 出来的。

1.2.2 单例模式(Spring Bean 默认作用域)

Spring 的单例不是传统意义上的 "写个 private 构造器 + static 实例" 那种单例,而是 容器级别的单例------由容器来保证同一个 id 的 Bean 只创建一次。

java 复制代码
// 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 的循环依赖问题,这个在面试中经常和单例模式一起追问。

注意Spring 的单例是非线程安全的。因为单例 Bean 在容器中只有一个实例,如果 Bean 里有可变状态,多线程并发访问就会出问题。所以 Spring 官方推荐 Bean 设计为 无状态 的。

1.2.3 代理模式(AOP 灵魂)

这个应该是大家最熟悉的,Spring AOP 的两种实现方式------JDK 动态代理和 CGLIB 代理,本质上都是代理模式的应用。

java 复制代码
// 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 能成立的基础。

1.2.4 模板方法模式(JdbcTemplate 一族)

模板方法模式在 Spring 中用得非常多,但凡名字里带 Template 的,基本都是。

java 复制代码
// 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 创建、异常处理、资源关闭 ------ 全部由模板搞定

模板方法模式的套路是:父类定义算法骨架(固定流程),子类实现可变步骤。JdbcTemplateJDBC 那套繁琐的 try-catch-finally 流程全部封装好了,只需要告诉它 "SQL 是什么" 和 "结果怎么映射",其他的一概不用操心。

Spring 中类似的还有 RestTemplateJmsTemplateRedisTemplate,套路完全一样。

1.2.5 观察者模式(事件机制)

Spring 的事件机制就是标准的观察者模式:一个事件发布者,N 个事件监听者。

java 复制代码
// 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中的事件讲解

1.2.6 适配器模式(Spring MVC 的核心)

Spring MVCHandlerAdapter 就是典型的适配器模式。

上图的逻辑是:

  • DispatcherServlet 拿到一个 Handler 后,不知道它具体是什么类型(可能是传统 Controller 接口,可能是 @RequestMapping 注解方法,也可能是 HttpRequestHandler)。
  • 于是 DispatcherServlet 不直接调用 Handler,而是找到对应的 HandlerAdapter,由适配器来负责调用。每种 Handler 都有自己专属的适配器实现。
  • 这样 DispatcherServlet 只需要面向 HandlerAdapter 这个统一接口编程,完全不用关心底层 Handler 的差异。

这就是适配器模式的价值:让原本不兼容的接口能够协同工作。

1.2.7 策略模式

Spring 中策略模式的应用也很多,比如资源加载:

java 复制代码
// 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 接口编程,至于底层是从类路径读、从磁盘读、还是从网络读,由具体的策略实现来决定。

相关推荐
Refrain_zc1 小时前
Android TV 语音消息实战:遥控器 PCM 录音失真修复与扬声器强制播放方案
java
Stick_ZYZ1 小时前
从“能调用工具”到“能稳定执行任务”:Agent 工程化的下一步
java·人工智能·后端·spring·ai
代码中介商1 小时前
C++四大设计模式:单例、工厂、观察者、策略
java·c++·设计模式
宋志宗1 小时前
从三层架构到清晰边界:一套更适合复杂 Java 服务的分层方法
java
lulu12165440782 小时前
Codex Computer Use 深度分析:AI桌面自动化的技术突破与行业影响
java·运维·人工智能·自动化·ai编程
2401_872418782 小时前
什么是多范式编程语言?——以 C++ 为例深入理解编程范式
java·大数据·c++
一 乐2 小时前
人口老龄化社区服务与管理平台|基于springboot+vue的人口老龄化社区服务与管理平台(源码+数据库+文档)
java·数据库·vue.js·spring boot·论文·毕设·人口老龄化社区服务与管理平台
東雪木2 小时前
泛型、反射、注解(Spring 框架核心底层)专属复习笔记
java·windows·笔记·学习·spring
林森lsjs2 小时前
【日耕一题】3. 通过键盘输入一行字符,分别统计出其中英文字母、空格、数字和其它字符的个数。
java·开发语言