单例·工厂·策略·代理·Spring设计模式 | 每道题含答案+追问+代码 | 实习生面试通过率提升50%
高频必手写
面试官考察:DCL(Double-Check Locking)、volatile、构造私有化、序列化安全。
public class Singleton {
// volatile:禁止指令重排,防止半初始化对象
private static volatile Singleton instance;
private Singleton() {
// 防反射攻击
if (instance != null) throw new RuntimeException("已存在实例");
}
public static Singleton getInstance() {
if (instance == null) { // 第一重检查:减少锁竞争
synchronized (Singleton.class) {
if (instance == null) { // 第二重检查:确保只创建一次
instance = new Singleton();
}
}
}
return instance;
}
// 防序列化破坏单例
protected Object readResolve() { return getInstance(); }
}
new Singleton() 分三步:分配内存→初始化对象→赋值给instance。JIT可能重排后两步,导致其他线程读到未初始化的对象。volatile禁止这个重排序。
枚举单例更简单安全(天生防序列化、防反射),但不能懒加载也不能继承。DCL灵活但代码略复杂。Josh Bloch推荐枚举。
必问
| 维度 | 简单工厂 | 工厂方法 | 抽象工厂 |
|---|---|---|---|
| 复杂度 | 低 | 中 | 高 |
| 创建对象数 | 单个产品 | 单个产品族 | 多个产品族 |
| 扩展性 | 差(改工厂类) | 好(加新工厂) | 好(加新工厂族) |
| 开闭原则 | 违反 | 遵循 | 遵循 |
| Spring对标 | BeanFactory | FactoryBean | ApplicationContext |
BeanFactory(简单工厂)、FactoryBean接口(工厂方法)、ApplicationContext(抽象工厂——创建一整套Bean)。
场景设计
// 策略接口
interface PaymentStrategy {
void pay(double amount);
}
// 具体策略
class AlipayStrategy implements PaymentStrategy {
public void pay(double amount) { System.out.println("支付宝付" + amount); }
}
class WechatPayStrategy implements PaymentStrategy {
public void pay(double amount) { System.out.println("微信付" + amount); }
}
// 上下文——组合策略
class PaymentContext {
private PaymentStrategy strategy;
public PaymentContext(PaymentStrategy s) { this.strategy = s; }
public void execute(double amount) { strategy.pay(amount); }
}
// 使用
PaymentContext ctx = new PaymentContext(new AlipayStrategy());
ctx.execute(100);
策略模式是客户端主动选择用哪个算法(外部切换),状态模式是对象自身根据状态自动切换行为(内部驱动)。
框架应用
一对多依赖关系,一个对象变化时通知所有依赖者。典型场景:事件监听、消息推送、数据变更通知。
观察者模式。ApplicationEvent(事件)→ ApplicationListener(监听器/观察者)→ ApplicationContext(发布者/被观察者)。@EventListener注解就是观察者模式的Spring实现。
观察者模式是同进程内同步通知,发布-订阅通过消息中间件(如MQ)异步解耦,跨进程通信。
Spring核心
| 维度 | JDK动态代理 | CGLIB代理 |
|---|---|---|
| 实现方式 | 实现接口 | 继承父类 |
| 核心类 | Proxy + InvocationHandler | Enhancer + MethodInterceptor |
| 要求 | 必须有接口 | 类不能是final |
| 性能 | 反射,稍慢 | 字节码生成,更快 |
| Spring默认 | 接口用JDK | 无接口用CGLIB |
默认CGLIB。配置 spring.aop.proxy-target-class=true(默认就是true)。因为CGLIB不需要接口,更灵活。
IO框架
Java IO流就是装饰器模式的最佳案例:
// BufferedReader 装饰了 FileReader,在不修改原类的情况下增加了缓冲功能
BufferedReader br = new BufferedReader(new FileReader("test.txt"));
// 层层装饰:缓冲→输入流→文件
// 每一层只加一个新功能,不改变原类的接口
装饰器是增强功能(加法),代理是控制访问(控制权)。装饰器可以无限叠,代理通常一对一。但代码结构几乎一样——区别在意图。
框架设计
// AbstractApplicationContext.refresh() 就是模板方法
public void refresh() {
prepareRefresh(); // ①准备
obtainFreshBeanFactory(); // ②获取BeanFactory
prepareBeanFactory(); // ③准备BeanFactory
postProcessBeanFactory(); // ④后处理(钩子方法)
invokeBeanFactoryPostProcessors(); // ⑤执行后处理器
registerBeanPostProcessors(); // ⑥注册Bean后处理器
// ...更多步骤
finishRefresh(); // ⑦完成刷新
// 子类通过重写 postProcessBeanFactory() 来定制行为
}
钩子方法有默认空实现(子类可选重写),抽象方法必须子类实现。钩子给你"可以选择"的灵活性。
兼容改造
类适配器(继承)和对象适配器(组合)。Java只支持对象适配器(单继承限制)。
// 对象适配器——推荐
interface Target { void request(); }
class Adaptee { void specificRequest() { System.out.println("原有方法"); } }
class Adapter implements Target {
private Adaptee adaptee = new Adaptee();
public void request() { adaptee.specificRequest(); } // 转换调用
}
适配器模式。DispatcherServlet不关心具体Handler类型(Controller/HttpRequestHandler),通过HandlerAdapter统一调用。
Web应用
Servlet Filter链、Spring Interceptor链都是责任链模式。每个拦截器处理完调用chain.doFilter()传给下一个。
// Spring拦截器链伪代码
class HandlerExecutionChain {
HandlerInterceptor[] interceptors;
int index = -1;
boolean applyPreHandle(request, response) {
for (int i = 0; i < interceptors.length; i++) {
if (!interceptors[i].preHandle(request, response, handler)) {
triggerAfterCompletion(request, response);
return false; // 被某个拦截器终止
}
}
return true;
}
}
用链表结构,每个节点持有下一节点的引用。LinkedList天然支持动态增删。核心:每个节点执行完自己的逻辑后调用next.handle()。
必考总结
| 模式 | Spring中的应用 |
|---|---|
| 单例 | Bean默认单例(scope=singleton) |
| 工厂 | BeanFactory、FactoryBean |
| 代理 | AOP的JDK/CGLIB代理 |
| 模板方法 | AbstractApplicationContext.refresh()、JdbcTemplate |
| 观察者 | ApplicationEvent + @EventListener |
| 适配器 | HandlerAdapter、AdvisorAdapter |
| 策略 | Resource接口的不同实现(ClassPathResource/FileUrlResource) |
| 装饰器 | BeanDefinitionDecorator、ServerHttpRequestDecorator |
| 责任链 | Interceptor链、Filter链 |
| 建造者 | RestTemplateBuilder、MockMvcBuilders |
依赖注入(DI)本身不是GoF23种设计模式之一,但它实现了"好莱坞原则"——Don't call us, we'll call you。本质是控制反转(IoC)的实现。
🔟 道题全背完,设计模式面试不再怕!
如果这些内容帮到了你 →
☕ 请杯咖啡,持续产出更多干货