核心速览:代理模式是结构型设计模式中的经典,通过引入代理对象间接访问目标对象,实现功能增强与访问控制。本文从痛点切入,详解静态代理与动态代理的概念差异、底层原理、代码实战及高频面试题,帮助读者建立完整知识链路。
2026年4月,AI助手正在深刻改变我们与软件的交互方式——以DeepSeek为代表的AI代理技术,正从传统语言模型向具备自主执行能力的智能体演进-31。而在技术底层,支撑这一切运作的核心设计模式之一,正是今天我们要深入探讨的 代理模式(Proxy Pattern) 。本文将通过技术科普+原理剖析+代码示例+面试要点的完整链路,帮你彻底理解这一结构型设计模式,建立从概念到应用的全方位认知。

一、痛点切入:为什么需要代理模式?
在日常开发中,我们经常面临这样的场景:需要在某个方法执行前后添加额外的逻辑,比如权限校验、日志记录、事务管理等。

传统实现方式的代码示例:
// 传统的直接实现方式 public class OrderService { public void createOrder(Order order) { // 权限校验(重复代码) if (!checkPermission()) { throw new SecurityException("无权限"); } // 日志记录(重复代码) System.out.println("开始创建订单..."); // 核心业务逻辑 doCreateOrder(order); // 日志记录(重复代码) System.out.println("订单创建完成"); } }
分析这种实现方式的痛点:
代码冗余:权限校验、日志等逻辑在每个方法中重复出现
耦合度高:业务逻辑与非业务逻辑(日志、权限)混杂在一起
扩展性差:如果需要新增缓存功能,每个方法都要改动
维护困难:修改日志格式时,需要在所有类中逐一修改
代理模式正是为解决这一矛盾而诞生的设计模式-12。它提供了一种既不造成代码冗余、又不引入耦合的方式,实现横切关注点的集中管理。
二、核心概念讲解:代理模式(Proxy Pattern)
定义:代理模式(Proxy Pattern)是一种结构型设计模式,为其他对象提供一种代理,以控制对这个对象的访问-43。
拆解关键词:
代理对象:在客户端和目标对象之间充当中介,负责转发请求并可在前后进行额外处理-7
控制访问:代理不替代原对象执行核心逻辑,而是作为访问的“门禁”,在不侵入目标对象内部结构的前提下,承担权限校验、调用拦截、行为增强等职责
生活化类比:代理模式本质上和我们生活中的中介、代购、经纪人完全一致-43。比如租客找房子——租客(客户端)不直接找房东(真实业务对象),而是找房产中介(代理对象)。中介帮房东带看、签合同、收押金,租客只和中介沟通,房东只负责收租。核心规律:代理不创造核心能力,最终的核心业务还是由真实对象完成-43。
核心价值:代理模式的核心价值在于——不修改目标对象的原有代码,通过代理对象实现在核心方法前后添加额外业务逻辑,符合设计模式的开闭原则和单一职责原则-41。
三、关联概念讲解:静态代理与动态代理
代理模式根据代理类的创建时机,可分为两类:静态代理和动态代理。
3.1 静态代理
定义:静态代理中,代理类在编译期就已编写好,代理类与真实类一一对应,编译完成后代理类就是一个固定的class文件-41。
代码示例:
// 1. 抽象主题接口 public interface Service { void doWork(); } // 2. 真实主题:核心业务逻辑 public class RealService implements Service { @Override public void doWork() { System.out.println("执行核心业务逻辑"); } } // 3. 静态代理类 public class StaticProxy implements Service { private RealService realService; public StaticProxy(RealService realService) { this.realService = realService; } @Override public void doWork() { // 前置增强:日志记录 System.out.println("【前置】开始执行"); // 调用真实对象 realService.doWork(); // 后置增强:日志记录 System.out.println("【后置】执行完成"); } } // 客户端调用 public class Client { public static void main(String[] args) { RealService real = new RealService(); StaticProxy proxy = new StaticProxy(real); proxy.doWork(); // 客户端只与代理交互 } }
局限性:静态代理虽然实现简单,但缺点明显——每个真实对象都需要一个对应的代理类,业务复杂时代理类数量会激增;一旦接口新增方法,目标对象和代理对象都要同步修改-。
3.2 动态代理
定义:动态代理在运行期由JVM通过反射机制动态生成代理类的字节码,无需手动编写代理类代码,一个动态代理类可以为任意多个真实类提供代理服务-41。
核心原理:动态代理是运行时生成字节码,区别于静态代码增强。Proxy.newProxyInstance()的核心流程包括:1)根据传入的接口信息拼装生成代理类字节码;2)将字节码加载进JVM生成Class对象;3)通过反射调用构造函数生成代理类实例-21。
代码示例(JDK动态代理) :
// 接口必须存在(JDK动态代理的要求) public interface UserService { void saveUser(String name); } public class UserServiceImpl implements UserService { @Override public void saveUser(String name) { System.out.println("保存用户: " + name); } } // InvocationHandler:核心增强逻辑 import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class LogInvocationHandler implements InvocationHandler { private Object target; // 持有真实对象的引用 public LogInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("【前置】方法: " + method.getName()); Object result = method.invoke(target, args); // 反射调用真实方法 System.out.println("【后置】方法执行完毕"); return result; } } // 使用动态代理 public class DynamicProxyDemo { public static void main(String[] args) { UserService realService = new UserServiceImpl(); UserService proxy = (UserService) Proxy.newProxyInstance( realService.getClass().getClassLoader(), realService.getClass().getInterfaces(), new LogInvocationHandler(realService) ); proxy.saveUser("张三"); } }
四、概念关系与区别总结
静态代理 vs 动态代理:静态代理是静态代理和动态代理的核心区别在于代理对象的创建时机和方式——静态代理需手动编写代理类,编译期确定;动态代理在运行期动态生成字节码-14。
一句话概括:静态代理是“编码时约定”,动态代理是“运行时即兴”。
| 对比维度 | 静态代理 | JDK动态代理 | CGLIB动态代理 |
|---|---|---|---|
| 实现原理 | 手动编写代理类 | 基于接口+反射生成代理类 | 基于继承+字节码生成子类 |
| 代理类创建时机 | 编译期 | 运行期 | 运行期 |
| 接口要求 | 必须实现统一接口 | 目标类必须实现接口 | 不需要接口,但类和方法不能是final |
| 代码冗余 | 每个目标类对应一个代理类 | 一个InvocationHandler代理所有 | 一个MethodInterceptor代理所有 |
| 典型应用 | 简单固定场景 | Spring AOP默认方案 | 无接口场景的AOP |
五、底层原理与技术支撑
动态代理的底层实现依赖三个关键技术点:
反射机制:JDK动态代理通过
java.lang.reflect.Proxy和InvocationHandler实现,核心调用是method.invoke(target, args)-12。反射机制允许在运行时动态获取类信息并调用方法,是实现动态代理的基础。字节码生成:JDK动态代理在运行时通过
ProxyGenerator.generateProxyClass拼装字节码,生成的代理类名通常为$Proxy0-21。CGLIB则基于ASM字节码操作框架,在运行时动态生成目标类的子类-22。类加载器:生成的字节码需要通过类加载器加载到JVM的方法区(元空间)中,才能成为可用的Class对象-21。
💡 理解这些底层原理,后续学习Spring AOP源码时会更加通透——Spring AOP的底层正是依赖动态代理技术实现的。
六、高频面试题与参考答案
面试题1:请谈谈代理模式的优缺点
参考答案:
优点:
符合开闭原则,可在不修改目标对象代码的前提下灵活扩展功能
职责分离,将访问控制与业务逻辑解耦,降低了系统耦合度
可实现智能化控制,如延迟加载、权限校验、缓存等
缺点:
增加了代理层,可能影响请求处理速度,带来性能开销
静态代理会导致类数量膨胀,增加系统复杂度
⭐ 踩分点:务必答出“开闭原则”“职责分离”“静态代理类膨胀”三个关键词。
面试题2:静态代理和动态代理有什么区别?JDK动态代理和CGLIB有什么区别?
参考答案:
静态代理与动态代理的核心区别在于代理类的创建时机和方式——静态代理在编译期手动编写代理类,代理类与真实类一一对应;动态代理在运行期由JVM通过反射/字节码技术动态生成代理类,一个动态代理可服务多个真实类。
JDK动态代理与CGLIB的区别:
实现原理:JDK基于接口+反射,CGLIB基于继承+字节码生成子类-23
接口要求:JDK要求目标类必须实现接口;CGLIB无此要求,但不能代理final类和方法-23
性能:JDK生成代理对象较快但反射调用略慢;CGLIB生成较慢但方法调用直接执行-23
依赖:JDK为Java原生支持;CGLIB需引入第三方库(Spring Core已内置)
⭐ 踩分点:区别要从“原理”“接口要求”“性能”“依赖”四个维度作答,层层递进。
面试题3:Spring AOP默认使用哪种动态代理?什么情况下会切换?
参考答案:
Spring AOP默认优先使用JDK动态代理。当目标对象没有实现任何接口时,Spring会自动切换为CGLIB动态代理。也可以通过配置proxyTargetClass=true强制使用CGLIB代理-22。
⭐ 踩分点:必须明确“优先JDK”“无接口自动切换CGLIB”这两个关键点。
面试题4:代理模式在实际开发中有哪些典型应用场景?
参考答案:
Spring AOP:通过动态代理实现声明式事务、日志、缓存等功能,这也是代理模式最广泛的应用场景
RPC框架:如Dubbo、gRPC,通过动态代理实现远程方法调用的透明化,让调用远程方法像调用本地方法一样-44
权限控制:保护代理在调用前验证调用者权限
懒加载:虚拟代理延迟创建开销较大的对象(如Hibernate的延迟加载)
日志记录与性能监控:在方法调用前后自动记录日志或统计耗时
⭐ 踩分点:答出“Spring AOP”“RPC框架”“懒加载”三个典型场景即可得分。
七、结尾总结
回顾本文核心知识点:
代理模式是一种结构型设计模式,通过引入代理对象间接访问目标对象,在不修改原代码的前提下实现功能增强
静态代理简单直接但代码冗余,适合固定场景;动态代理灵活通用,是Spring AOP等框架的底层核心
JDK动态代理基于接口+反射,CGLIB基于继承+字节码,二者各有适用场景
动态代理底层依赖反射机制和字节码生成技术
面试中需掌握:优缺点、静态/动态区别、JDK/CGLIB对比、应用场景
重点提醒:千万不要混淆代理模式与装饰器模式——代理模式聚焦访问控制,装饰器模式聚焦功能叠加-46。
进阶预告:下一篇文章将深入Spring AOP的底层源码,剖析动态代理在事务管理中的完整实现流程,敬请期待。
📝 本文编写于2026年4月,涵盖代理模式核心知识点。内容由AI城市助手结合最新技术资料整理生成,适合技术学习、面试备考及开发实践参考。