代理模式在Java设计模式体系中占据着举足轻重的地位,属于结构型模式中的核心成员,面试中出现频率高达★★★★☆-6。然而很多开发者在使用层面存在误区——只会调用AOP却不懂底层原理、混淆静态代理与动态代理、分不清代理模式和装饰器模式、面试时回答缺乏逻辑层次。
本文将从痛点切入→核心概念→代码示例→底层原理→面试要点五个维度,带你一次性吃透代理模式。后续还将陆续推出适配器模式、装饰器模式等结构型模式系列文章,敬请关注。

一、痛点切入:为什么需要代理模式?
先看一个最常见的场景:你有一个订单服务接口,现在需要在执行下单方法前后添加日志记录和权限校验。

传统做法:直接修改业务代码
public class OrderServiceImpl implements OrderService { @Override public Map executeFoodOrder(String userName, Map<String,Integer> foodMap) { // 日志记录 System.out.println("开始记录日志..."); // 权限校验 System.out.println("开始校验权限..."); // 核心业务 System.out.println("执行下单核心业务..."); // 日志记录 System.out.println("结束记录日志..."); return resultMap; } }
痛点分析:
耦合高:日志、权限等代码与业务逻辑耦合在一起,代码冗余
扩展性差:每新增一个方法或修改需求,都要修改多个实现类
维护困难:日志和权限逻辑分散在各个业务方法中,难以统一管理
违背开闭原则:对修改开放,而非对扩展开放
代理模式的设计初衷正是解决上述痛点——为其他对象提供一种代理以控制对这个对象的访问-。通俗理解:你不想亲自做某件事,就找个代理替你完成。比如滴滴代驾(代理)帮你把车开到目的地;房屋中介(代理)帮你完成租房交易-1。
二、核心概念讲解:代理模式(Proxy Pattern)
标准定义
代理模式(Proxy Pattern) ,也叫委托模式,属于结构型设计模式,为其他对象提供一种代理以控制对这个对象的访问-。
角色拆解
| 角色 | 英文名 | 职责 |
|---|---|---|
| 抽象主题 | Subject | 定义真实对象和代理对象的共同接口,通常为接口或抽象类 |
| 真实主题 | RealSubject | 真正执行业务逻辑的对象,即被代理的目标对象 |
| 代理主题 | Proxy | 持有真实对象的引用,在调用前后添加附加操作,控制访问 |
生活化类比
你喝醉了需要回家,叫了滴滴代驾——代驾师傅就是你的代理,代驾师傅帮你开车(控制访问),你和目的地之间隔了一个代理;你不用亲自开车,代驾师傅替你做这件事,并且还能在你到达前帮你检查车况(前置增强)、帮你停好车(后置增强)-1。
核心价值
代理模式的核心不是“增强行为”,而是“控制访问”-56。它主要解决三大问题:
隔离客户端与目标对象,降低耦合度
在不修改目标对象的前提下,扩展功能
对目标对象进行访问控制,如权限校验、延迟加载等
三、关联概念讲解:静态代理 vs 动态代理
代理模式分为静态代理和动态代理两大阵营。
静态代理
定义:在程序编译时就确定了代理类的类型,需要手动编写代理类-19。
实现机制:代理类和目标类实现同一个接口,代理类持有目标对象的引用,在调用目标方法前后添加额外逻辑-。
代码示例:
// 1. 定义接口 public interface SmsService { String send(String message); } // 2. 真实实现类 public class SmsServiceImpl implements SmsService { @Override public String send(String message) { System.out.println("send message:" + message); return message; } } // 3. 静态代理类(需要手动编写) public class SmsProxy implements SmsService { private final SmsService smsService; public SmsProxy(SmsService smsService) { this.smsService = smsService; } @Override public String send(String message) { System.out.println("before method send()"); // 前置增强 smsService.send(message); // 调用目标方法 System.out.println("after method send()"); // 后置增强 return null; } } // 4. 客户端调用 SmsService smsService = new SmsServiceImpl(); SmsProxy proxy = new SmsProxy(smsService); proxy.send("java");
执行结果:
before method send() send message:java after method send()
动态代理——JDK动态代理
定义:在程序运行时,运用Java反射机制动态创建代理类,无需手动编写代理类-。
核心类:Proxy + InvocationHandler-
代码示例:
// 1. 定义InvocationHandler public class LoggingInvocationHandler implements InvocationHandler { private final Object target; // 持有被代理对象 public LoggingInvocationHandler(Object target) { this.target = target; } @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; } } // 2. 动态创建代理对象 SmsService target = new SmsServiceImpl(); LoggingInvocationHandler handler = new LoggingInvocationHandler(target); SmsService proxy = (SmsService) Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), handler ); proxy.send("java");
动态代理——CGLIB动态代理
定义:CGLIB(Code Generation Library)是一个基于ASM的字节码生成库,通过继承方式为没有实现接口的类创建代理-44。
核心类:Enhancer + MethodInterceptor-44
代码示例:
// 1. 定义目标类(无需实现接口) public class AliSmsService { public String send(String message) { System.out.println("send message:" + message); return message; } } // 2. 定义MethodInterceptor public class DebugMethodInterceptor implements MethodInterceptor { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("before method " + method.getName()); // 前置增强 Object object = proxy.invokeSuper(obj, args); // 调用父类方法 System.out.println("after method " + method.getName()); // 后置增强 return object; } } // 3. 通过Enhancer创建代理 Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(AliSmsService.class); enhancer.setCallback(new DebugMethodInterceptor()); AliSmsService proxy = (AliSmsService) enhancer.create(); proxy.send("java");
四、概念关系与区别总结
静态代理 vs 动态代理
| 对比维度 | 静态代理 | JDK动态代理 | CGLIB动态代理 |
|---|---|---|---|
| 代理创建时机 | 编译时 | 运行时 | 运行时 |
| 是否需要实现接口 | 必须实现 | 必须实现 | 不需要 |
| 依赖第三方库 | 否 | 否(JDK内置) | 是(需引入cglib) |
| 实现原理 | 手动编写代理类 | 反射动态生成 | 字节码生成子类 |
| 灵活性 | 低(接口新增方法需同步修改) | 高 | 高 |
| 适用场景 | 极少使用 | 目标类有接口 | 目标类无接口 |
一句话记忆:静态代理——编译时手写;JDK动态——反射借接口;CGLIB——继承生子类。
代理模式 vs 装饰器模式
| 对比维度 | 代理模式 | 装饰器模式 |
|---|---|---|
| 核心目的 | 控制对对象的访问 | 给对象动态添加额外功能 |
| 关注点 | 访问控制与管理 | 功能增强 |
| 代理/装饰对象创建 | 代理内部通常自己创建或控制真实对象 | 装饰器由外部传入被装饰对象 |
| 链条特性 | 通常只有一层代理 | 可以形成多条装饰链 |
| 典型场景 | 权限校验、延迟加载、日志记录 | IO流嵌套、给对象动态添加职责 |
核心辨别法:看你的目的——需要控制访问用代理,需要叠加功能用装饰器-48。
五、底层原理与技术支撑
JDK动态代理底层原理
JDK动态代理的核心依赖Java反射机制。当调用Proxy.newProxyInstance()时,JVM在运行时动态生成代理类的Class文件,该代理类实现了指定接口。当调用代理对象的方法时,会被统一转发到InvocationHandler.invoke()方法中执行-32。
关键点:invoke()方法中必须显式调用method.invoke(target, args),否则目标逻辑不会被执行-56。
CGLIB动态代理底层原理
CGLIB基于ASM字节码操作库,在运行时动态生成被代理类的子类,并通过拦截器技术拦截所有父类方法的调用,在子类中织入横切逻辑-44。
局限性:不能代理final修饰的类或方法,因为CGLIB通过继承实现代理。
Spring AOP中的应用
Spring AOP正是基于动态代理实现的:
如果目标类实现了接口,默认使用JDK动态代理
如果目标类没有实现接口,则使用CGLIB动态代理(可通过
proxyTargetClass=true强制使用CGLIB)-32
六、应用场景速查
| 场景 | 描述 | 代理类型 |
|---|---|---|
| 远程代理 | 为不同地址空间的对象提供本地代表,如RPC调用 | 动态代理 |
| 虚拟代理 | 按需创建开销大的对象,延迟实例化 | 静态/动态 |
| 保护代理 | 控制对真实对象的访问权限,进行安全检查 | 动态代理 |
| 日志记录 | 在方法执行前后记录日志 | JDK动态/CGLIB |
| Spring AOP | 事务管理、权限控制、性能监控等横切关注点 | JDK/CGLIB |
| 延迟加载 | 直到真正需要时才加载对象数据 | 虚拟代理 |
代理模式在Spring框架和Dubbo框架中都有广泛应用——Spring通过代理实现AOP功能,Dubbo通过动态代理生成接口的远程调用代理类,隐藏了远程通信的复杂性-60。
七、高频面试题与参考答案
面试题1:说一下代理模式及其分类?
参考答案:
代理模式(Proxy Pattern)也叫委托模式,属于结构型设计模式。它为其他对象提供一种代理以控制对这个对象的访问-。主要解决在不修改目标对象的前提下,对方法调用进行扩展和控制。
代理模式分为两大类:
静态代理:编译时手动编写代理类,代理类和目标类实现同一接口
动态代理:运行时动态生成代理类,进一步分为JDK动态代理(基于接口)和CGLIB动态代理(基于继承)
踩分点:先说定义和归属 → 说核心价值 → 说出分类 → 每类一句话描述。
面试题2:JDK动态代理和CGLIB动态代理的区别?
参考答案:
| 区别点 | JDK动态代理 | CGLIB动态代理 |
|---|---|---|
| 实现原理 | 基于反射和Proxy类 | 基于ASM字节码生成子类 |
| 目标类要求 | 必须实现至少一个接口 | 无需实现接口 |
| 依赖 | JDK内置 | 需要引入cglib库 |
| 代理限制 | 只能代理接口方法 | 不能代理final类/方法 |
| Spring默认策略 | 有接口时使用 | 无接口时使用 |
踩分点:先说两个本质区别(有接口/无接口)→ 再说原理差异(反射 vs 字节码)→ 补充适用场景。
面试题3:代理模式和装饰器模式的区别?
参考答案:
目的不同:代理模式控制对对象的访问,装饰器模式为对象动态添加功能-48
关注点不同:代理关注访问控制,装饰器关注功能增强
对象创建方式不同:代理通常自己创建或控制真实对象,装饰器由外部传入被装饰对象
链式特性不同:代理通常只有一层,装饰器可形成多条装饰链
一句话总结:控制访问用代理,叠加功能用装饰器。
面试题4:Spring AOP使用的是哪种代理?
参考答案:
Spring AOP底层基于动态代理实现,支持两种方式:
目标类实现接口时,默认使用JDK动态代理,通过
java.lang.reflect.Proxy创建代理对象目标类未实现接口时,使用CGLIB动态代理,通过生成子类的方式实现代理
可通过配置
proxyTargetClass=true强制使用CGLIB代理-32
踩分点:先说AOP基于代理模式 → 再说两种代理的选择策略 → 补充强制配置方式。
面试题5:代理模式有哪些应用场景?
参考答案:
远程代理:RPC框架中为远程对象提供本地代表
虚拟代理:Hibernate的延迟加载,需要时才加载数据库对象
保护代理:权限校验,访问真实对象前进行安全检查
日志/监控代理:AOP中的日志记录、性能监控
Spring事务管理:通过代理在方法前后开启/提交事务-60
踩分点:分点列举 → 每个场景一句话说明 → 举例(Spring、Hibernate等)。
八、结尾总结
核心知识点回顾
| 知识点 | 核心要点 |
|---|---|
| 代理模式定义 | 为其他对象提供代理以控制访问,属于结构型模式 |
| 静态代理 | 编译时手动编写代理类,灵活性低,极少使用 |
| JDK动态代理 | 反射机制 + Proxy + InvocationHandler,目标类必须有接口 |
| CGLIB动态代理 | 字节码生成 + 继承 + MethodInterceptor,目标类不能是final |
| Spring AOP策略 | 有接口用JDK,无接口用CGLIB |
| 与装饰器区别 | 代理控制访问,装饰器增强功能 |
重点与易错点
静态代理:接口新增方法时,代理类必须同步修改,违背开闭原则
JDK动态代理:
invoke()中必须调用method.invoke(target, args),否则目标逻辑不执行CGLIB代理:不能代理
final修饰的类和方法概念区分:代理模式的核心是“控制访问”,不是“增强行为”
进阶预告
下一篇将深入讲解装饰器模式,分析它与代理模式的异同以及在Java IO流中的应用,敬请期待。
文末互动:你在项目中用过哪种代理方式?遇到过哪些坑?欢迎在评论区留言交流。
