电子展会
HOME
电子展会
正文内容
私信ai助手:2026年4月最新Java代理模式从静态代理到动态代理深度解析
发布时间 : 2026-05-11
作者 : 小编
访问数量 : 6
扫码分享至微信

代理模式是Java框架底层应用最广泛的设计模式之一,但很多开发者长期陷入“只会用、不懂原理”的困境——面试被问“JDK动态代理为什么只能代理接口”时答不上来,写AOP时也分不清何时用JDK何时用CGLIB。本文将从静态代理→JDK动态代理→CGLIB动态代理逐层递进,讲清核心概念、写出可运行代码、剖析底层原理、汇总高频面试题,帮你建立完整知识链路。


一、痛点切入:为什么需要代理模式?

先看一个最常见的场景:为UserService添加日志记录功能。很多初学者会直接在业务方法内部写日志代码:

java
复制
下载
public class UserServiceImpl {

public void saveUser() { System.out.println("[日志]保存用户方法开始执行"); // 核心业务逻辑 System.out.println("保存用户数据"); System.out.println("[日志]保存用户方法执行结束"); } }

这段代码有什么问题?

  1. 耦合高:日志代码与业务代码纠缠在一起,修改日志格式需要改动业务类

  2. 代码冗余:每个需要日志的方法都要重复编写类似代码

  3. 扩展性差:若需新增事务、权限校验等功能,每个方法都得改一遍

  4. 违反单一职责原则:业务类既要管核心逻辑,又要管辅助功能

为了解决这些问题,代理模式应运而生——在客户端和目标对象之间引入一个代理对象,由代理对象统一处理辅助逻辑,业务类只需专注核心功能。


二、概念 A:静态代理

标准定义

静态代理(Static Proxy) :代理类在编译期就已确定,需要为每个目标类显式编写对应的代理类,代理类与目标类实现相同的接口,并在内部持有目标对象的引用-4

生活化类比

代理模式在生活中随处可见:火车站售票与代售点。火车站负责核心卖票业务,代售点作为代理——它先收取服务费(前置增强),再把请求转发给火车站(调用目标方法)-1

代码示例

java
复制
下载
// 1. 抽象主题:统一业务接口
public interface UserService {
    void saveUser();
}

// 2. 真实主题:只负责核心业务
public class UserServiceImpl implements UserService {
    @Override
    public void saveUser() {
        System.out.println("保存用户数据");
    }
}

// 3. 静态代理类:实现同一接口,持有目标对象引用
public class UserServiceProxy implements UserService {
    private UserService target;  // 持有目标对象
    
    public UserServiceProxy(UserService target) {
        this.target = target;
    }
    
    @Override
    public void saveUser() {
        System.out.println("[静态代理]前置日志");  // 前置增强
        target.saveUser();                        // 调用目标方法
        System.out.println("[静态代理]后置事务");  // 后置增强
    }
}

// 4. 客户端调用
public class Client {
    public static void main(String[] args) {
        UserService target = new UserServiceImpl();
        UserService proxy = new UserServiceProxy(target);
        proxy.saveUser();
    }
}

优缺点分析

优点缺点
符合开闭原则,无需修改目标对象类爆炸:每个业务类都要写一个代理类-4
职责清晰,辅助逻辑集中在代理类接口耦合:代理类必须实现与目标类相同的接口
编译期检查,类型安全有保障维护成本高:接口新增方法,代理类和目标类都得改-

随着项目规模扩大,上述缺点会变得非常突出——这就是动态代理诞生的直接原因。


三、概念 B:动态代理

标准定义

动态代理(Dynamic Proxy) :代理类在运行时由JVM动态生成,无需手动编写代理类代码。一个动态代理类可以为任意多个真实类提供代理服务-56

动态代理的两大实现方式

对比维度JDK动态代理CGLIB动态代理
代理方式基于接口实现基于继承生成子类
目标对象要求必须实现至少一个接口无接口要求,但不能是final类
底层技术反射 + ProxyASM字节码生成框架
性能特点JDK 8后性能持续优化生成类稍慢,调用接近直接调用-38
依赖Java标准库(无需额外依赖)需引入CGLIB库
限制只能代理接口中定义的方法无法代理final类和final方法-27

JDK动态代理代码示例

java
复制
下载
// 1. 目标接口
public interface UserService {
    void saveUser();
}

// 2. 目标类
public class UserServiceImpl implements UserService {
    @Override
    public void saveUser() {
        System.out.println("保存用户数据");
    }
}

// 3. InvocationHandler:代理逻辑处理器
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;
    }
}

// 4. 客户端调用
public class Client {
    public static void main(String[] args) {
        UserService target = new UserServiceImpl();
        UserService proxy = (UserService) Proxy.newProxyInstance(
            target.getClass().getClassLoader(),  // 类加载器
            target.getClass().getInterfaces(),   // 代理类要实现的接口列表
            new LogInvocationHandler(target)      // 调用处理器
        );
        proxy.saveUser();
    }
}

CGLIB动态代理代码示例

java
复制
下载
// 引入Maven依赖
// <dependency>
//     <groupId>cglib</groupId>
//     <artifactId>cglib</artifactId>
//     <version>3.2.6</version>
// </dependency>

// 1. 目标类(无需实现接口)
public class UserService {
    public void saveUser() {
        System.out.println("保存用户数据");
    }
}

// 2. MethodInterceptor:方法拦截器
public class LogMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("[CGLIB]前置处理:" + method.getName());
        Object result = proxy.invokeSuper(obj, args);  // 调用父类(目标类)方法
        System.out.println("[CGLIB]后置处理");
        return result;
    }
}

// 3. 客户端调用
public class Client {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(UserService.class);        // 设置父类(目标类)
        enhancer.setCallback(new LogMethodInterceptor()); // 设置回调
        UserService proxy = (UserService) enhancer.create();
        proxy.saveUser();
    }
}

四、概念关系与区别总结

一句话总结:静态代理是“写死”的代理,动态代理是“运行时生成”的代理;JDK动态代理基于接口,CGLIB动态代理基于继承。

图表
代码
下载
全屏
.kvfysmfp{overflow:hidden;touch-action:none}.ufhsfnkm{transform-origin: 0 0}
mermaid-svg-4{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}mermaid-svg-4 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}mermaid-svg-4 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}mermaid-svg-4 .error-icon{fill:552222;}mermaid-svg-4 .error-text{fill:552222;stroke:552222;}mermaid-svg-4 .edge-thickness-normal{stroke-width:1px;}mermaid-svg-4 .edge-thickness-thick{stroke-width:3.5px;}mermaid-svg-4 .edge-pattern-solid{stroke-dasharray:0;}mermaid-svg-4 .edge-thickness-invisible{stroke-width:0;fill:none;}mermaid-svg-4 .edge-pattern-dashed{stroke-dasharray:3;}mermaid-svg-4 .edge-pattern-dotted{stroke-dasharray:2;}mermaid-svg-4 .marker{fill:333333;stroke:333333;}mermaid-svg-4 .marker.cross{stroke:333333;}mermaid-svg-4 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}mermaid-svg-4 p{margin:0;}mermaid-svg-4 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:333;}mermaid-svg-4 .cluster-label text{fill:333;}mermaid-svg-4 .cluster-label span{color:333;}mermaid-svg-4 .cluster-label span p{background-color:transparent;}mermaid-svg-4 .label text,mermaid-svg-4 span{fill:333;color:333;}mermaid-svg-4 .node rect,mermaid-svg-4 .node circle,mermaid-svg-4 .node ellipse,mermaid-svg-4 .node polygon,mermaid-svg-4 .node path{fill:ECECFF;stroke:9370DB;stroke-width:1px;}mermaid-svg-4 .rough-node .label text,mermaid-svg-4 .node .label text,mermaid-svg-4 .image-shape .label,mermaid-svg-4 .icon-shape .label{text-anchor:middle;}mermaid-svg-4 .node .katex path{fill:000;stroke:000;stroke-width:1px;}mermaid-svg-4 .rough-node .label,mermaid-svg-4 .node .label,mermaid-svg-4 .image-shape .label,mermaid-svg-4 .icon-shape .label{text-align:center;}mermaid-svg-4 .node.clickable{cursor:pointer;}mermaid-svg-4 .root .anchor path{fill:333333!important;stroke-width:0;stroke:333333;}mermaid-svg-4 .arrowheadPath{fill:333333;}mermaid-svg-4 .edgePath .path{stroke:333333;stroke-width:2.0px;}mermaid-svg-4 .flowchart-link{stroke:333333;fill:none;}mermaid-svg-4 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}mermaid-svg-4 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}mermaid-svg-4 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}mermaid-svg-4 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}mermaid-svg-4 .cluster rect{fill:ffffde;stroke:aaaa33;stroke-width:1px;}mermaid-svg-4 .cluster text{fill:333;}mermaid-svg-4 .cluster span{color:333;}mermaid-svg-4 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid aaaa33;border-radius:2px;pointer-events:none;z-index:100;}mermaid-svg-4 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:333;}mermaid-svg-4 rect.text{fill:none;stroke-width:0;}mermaid-svg-4 .icon-shape,mermaid-svg-4 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}mermaid-svg-4 .icon-shape p,mermaid-svg-4 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}mermaid-svg-4 .icon-shape rect,mermaid-svg-4 .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}mermaid-svg-4 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}mermaid-svg-4 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}mermaid-svg-4 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

代理模式

静态代理

动态代理

JDK动态代理
基于接口+反射

CGLIB动态代理
基于继承+字节码

对比项静态代理JDK动态代理CGLIB动态代理
代理类生成时机编译期运行期运行期
是否需要手动编写代理类
目标对象要求需实现接口必须实现接口无需接口(非final即可)
性能直接调用,最快反射调用,JDK8后显著优化生成类较慢,调用接近直接调用
灵活性
典型应用固定场景Spring AOP代理接口Spring AOP代理普通类、Hibernate懒加载

五、底层原理 / 技术支撑

JDK动态代理底层原理

JDK动态代理的本质是动态生成字节码 + 反射机制的结合-49

调用Proxy.newProxyInstance()时,JVM执行以下步骤:

  1. 查找或生成代理类:调用getProxyClass0(),先从WeakCache缓存中查找,若无则生成

  2. 生成代理类字节码:通过ProxyGenerator.generateProxyClass()动态生成代理类的二进制字节码,代理类命名格式为$Proxy0$Proxy1-67

  3. 加载代理类:通过类加载器将字节码加载到JVM中

  4. 创建代理实例:通过反射获取构造方法并实例化

生成的代理类继承java.lang.reflect.Proxy,实现了传入的所有接口。代理类中的每个方法都会将调用转发给InvocationHandler.invoke()方法-

关键源码流程

java
复制
下载
// Proxy.newProxyInstance() → getProxyClass0() → proxyClassCache.get()
// → ProxyClassFactory.apply() → 生成字节码 → defineClass0()加载

CGLIB动态代理底层原理

CGLIB(Code Generation Library)通过ASM字节码框架在运行时动态生成目标类的子类:

  1. 使用Enhancer设置目标类作为父类

  2. 通过ASM技术生成目标类的子类字节码,子类重写所有非final方法

  3. 方法调用被拦截到MethodInterceptor.intercept()方法

  4. intercept()中完成增强逻辑后,通过MethodProxy.invokeSuper()调用父类方法-27

静态代理的底层支撑

静态代理直接调用目标方法,不依赖反射或字节码生成,因此性能最高,但灵活性最差。

一句话说透:静态代理是编译期“写死”的调用链;JDK动态代理是运行期“拼出来”的字节码加反射调用;CGLIB是运行期“生出来”的子类加方法拦截。


六、高频面试题与参考答案

面试题1:代理模式是什么?有什么优缺点?

标准答案要点

  • 代理模式属于结构型设计模式,通过引入代理对象控制对目标对象的访问-

  • 优点:符合开闭原则,降低耦合度,可在不修改目标对象的前提下增强功能-56

  • 缺点:静态代理会产生大量代理类;增加代理对象会使请求处理速度略慢-58

面试题2:JDK动态代理为什么只能代理接口?

标准答案要点

  1. 生成的代理类已经继承了Proxy类,Java不支持多继承,所以无法再继承目标类

  2. 代理类通过实现目标接口来“冒充”目标对象

  3. 若目标类没有实现任何接口,JDK动态代理无法创建代理-58

面试题3:JDK动态代理和CGLIB动态代理的区别是什么?

标准答案要点

区别维度说明
代理方式JDK基于接口,CGLIB基于继承生成子类
目标要求JDK要求目标类实现接口;CGLIB无此要求,但不能代理final类/方法
底层技术JDK使用反射+Proxy;CGLIB使用ASM字节码生成
性能JDK8后两者差距缩小;CGLIB生成代理类较慢,但调用效率高
依赖JDK是标准库;CGLIB需额外引入
选择建议有接口优先用JDK,无接口或用Spring AOP时可自动切换-35

面试题4:Spring AOP中默认使用哪种代理?

标准答案要点

  • Spring AOP默认对接口实现类使用JDK动态代理

  • 若目标对象未实现任何接口,或强制使用CGLIB(proxy-target-class="true"),则使用CGLIB动态代理

  • Spring 4.0后,CGLIB代理的性能已大幅提升-

面试题5:静态代理和动态代理的根本区别是什么?

标准答案要点

  • 根本区别在于代理类的生成时机:静态代理在编译期生成,动态代理在运行期生成

  • 静态代理需要手动编写代理类代码,一个目标类对应一个代理类

  • 动态代理由JVM在运行时动态生成字节码,一个代理类可为多个目标类服务-56


七、结尾总结

核心知识点回顾

序号核心知识点
1代理模式通过引入代理对象控制对目标对象的访问,核心价值在于解耦功能增强
2静态代理在编译期确定代理关系,实现简单但扩展性差,会导致类爆炸
3JDK动态代理基于接口+反射,只能代理实现了接口的类
4CGLIB动态代理基于继承+ASM字节码,可代理无接口的普通类,但不能代理final类/方法
5底层原理上,JDK动态代理=字节码动态生成+反射调用;CGLIB=生成子类+方法拦截
6实际开发中,Spring AOP会根据目标对象是否实现接口自动选择合适的代理方式

重点与易错点

  • 易错点1:误以为JDK动态代理可以代理普通类——不行,目标类必须实现接口

  • 易错点2:误以为CGLIB可以代理所有类——不行,final类、final方法无法代理

  • 重点理解:静态代理 vs 动态代理的本质区别——代理类生成时机不同

  • 面试必考点:JDK vs CGLIB的代理方式、限制、底层原理

进阶预告

下一篇将深入Spring AOP底层源码,剖析Spring如何利用动态代理实现声明式事务管理,以及@Transactional注解失效的根本原因。欢迎持续关注!

王经理: 180-0000-0000(微信同号)
10086@qq.com
北京海淀区西三旗街道国际大厦08A座
©2026  上海羊羽卓进出口贸易有限公司  版权所有.All Rights Reserved.  |  程序由Z-BlogPHP强力驱动
网站首页
电话咨询
微信号

QQ

在线咨询真诚为您提供专业解答服务

热线

188-0000-0000
专属服务热线

微信

二维码扫一扫微信交流
顶部