Spring 中用到了哪些设计模式?
结论先行:Spring 几乎是一本"设计模式活教材",但面试要挑有代表性的讲:工厂(BeanFactory、FactoryBean)、单例(Bean 默认作用域)、模板方法(JdbcTemplate、refresh)、策略(Resource、HandlerMapping、InstantiationStrategy)、观察者(ApplicationEvent)、代理(AOP)、适配器(HandlerAdapter)、责任链(拦截器链)、装饰器(BeanWrapper、缓存装饰)、委派(BeanDefinitionParserDelegate)。
一、模式与落点对照
| 模式 | Spring 中的体现 | 解决的问题 |
|---|---|---|
| 工厂方法 | BeanFactory#getBean、FactoryBean#getObject | 把对象创建细节封装起来 |
| 单例 | 默认 singleton 作用域、singletonObjects 缓存 | 控制实例数量、复用对象 |
| 模板方法 | JdbcTemplate、RestTemplate、AbstractApplicationContext#refresh | 固定流程骨架,子类填步骤 |
| 策略 | Resource 实现族、HandlerMapping、PlatformTransactionManager | 同一接口多种算法可替换 |
| 观察者 | ApplicationEventPublisher + @EventListener | 解耦事件发布与处理 |
| 代理 | AOP、@Transactional、@Async | 无侵入增强方法 |
| 适配器 | HandlerAdapter、AdvisorAdapter、MessageConverter | 统一不同实现到同一调用方式 |
| 责任链 | HandlerExecutionChain、FilterChain、ClientHttpRequestInterceptor | 请求逐层处理 |
| 装饰器 | BeanWrapper、TransactionAwareCacheDecorator | 动态叠加职责 |
| 委派 | BeanDefinitionParserDelegate、DispatcherServlet 委派各组件 | 分离解析与调度 |
二、模板方法:refresh 就是骨架
// 伪代码,展示 refresh 的模板结构:父类定流程,子类实现可变步骤
public abstract class AbstractApplicationContext {
public void refresh() {
prepareRefresh(); // 可覆写
ConfigurableListableBeanFactory bf = obtainFreshBeanFactory();
prepareBeanFactory(bf);
postProcessBeanFactory(bf); // 钩子:子类扩展
invokeBeanFactoryPostProcessors(bf);
registerBeanPostProcessors(bf);
initMessageSource();
initApplicationEventMulticaster();
onRefresh(); // 钩子:Web 容器在此启动
registerListeners();
finishBeanFactoryInitialization(bf);
finishRefresh();
}
protected void onRefresh() { } // 空实现,留给子类
}
JdbcTemplate 也是同一套路:execute 里管连接的获取、异常转换、释放,把"设参数、取结果"留给 PreparedStatementSetter、RowMapper 回调。
三、观察者:事件解耦
// 1. 定义事件
public class OrderPaidEvent extends ApplicationEvent {
private final long orderId;
public OrderPaidEvent(Object source, long orderId) {
super(source);
this.orderId = orderId;
}
public long getOrderId() { return orderId; }
}
// 2. 发布事件(事务内发布,可用 @TransactionalEventListener 控制时机)
@Service
public class OrderService {
private final ApplicationEventPublisher publisher;
public OrderService(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
public void pay(long orderId) {
// ... 更新订单状态
publisher.publishEvent(new OrderPaidEvent(this, orderId));
}
}
// 3. 监听(默认同步执行,@Async 可异步)
@Component
public class CouponListener {
@EventListener
public void onPaid(OrderPaidEvent e) {
// 发券、加积分,与主流程解耦
}
}
四、策略 + 工厂:按类型取实现
public interface PayStrategy {
String type();
void pay(BigDecimal amount);
}
@Component
public class PayStrategyFactory {
private final Map<String, PayStrategy> strategies = new HashMap<>();
// 构造器注入 List,Spring 会把所有实现塞进来
public PayStrategyFactory(List<PayStrategy> list) {
for (PayStrategy s : list) {
strategies.put(s.type(), s);
}
}
public PayStrategy get(String type) {
return strategies.get(type);
}
}
五、常见追问
- 问:Spring 的单例是严格单例模式吗? 不是。它保证"每容器每 Bean 名一个实例",不同容器、不同 id 可有多个实例,且可被反射或
@Scope("prototype")打破。 - 问:
ApplicationEvent和 MQ 的区别? 前者是进程内同步/异步事件,无持久化、无跨服务能力;后者跨进程、可持久化、有投递语义。 - 问:为什么
HandlerAdapter算适配器? 因为 Controller 有@RequestMapping、HttpRequestHandler、Controller接口等多种形态,适配器把它们统一成handle调用。 - 问:能举一个反例吗?
DispatcherServlet本身更像"前端控制器 + 委派",不必强行套某个 GoF 模式,答清楚职责即可。
小结
答题结构:先给"工厂/单例/模板/策略/观察者/代理"六个必答,再各配一个 Spring 里的具体类名。能说出"模板方法体现在 refresh 的钩子"和"事件机制是观察者",说明真读过源码而不是背清单。