细石混凝土泵

一文吃透代理模式:2026年4月面试必问的Java设计模式终极指南

小编 2026-04-29 细石混凝土泵 2 0

代理模式在Java设计模式体系中占据着举足轻重的地位,属于结构型模式中的核心成员,面试中出现频率高达★★★★☆-6。然而很多开发者在使用层面存在误区——只会调用AOP却不懂底层原理、混淆静态代理与动态代理、分不清代理模式和装饰器模式、面试时回答缺乏逻辑层次。

本文将从痛点切入→核心概念→代码示例→底层原理→面试要点五个维度,带你一次性吃透代理模式。后续还将陆续推出适配器模式、装饰器模式等结构型模式系列文章,敬请关注。

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

先看一个最常见的场景:你有一个订单服务接口,现在需要在执行下单方法前后添加日志记录和权限校验

传统做法:直接修改业务代码

java
复制
下载
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。它主要解决三大问题:

  1. 隔离客户端与目标对象,降低耦合度

  2. 在不修改目标对象的前提下,扩展功能

  3. 对目标对象进行访问控制,如权限校验、延迟加载等

三、关联概念讲解:静态代理 vs 动态代理

代理模式分为静态代理动态代理两大阵营。

静态代理

定义:在程序编译时就确定了代理类的类型,需要手动编写代理类-19

实现机制:代理类和目标类实现同一个接口,代理类持有目标对象的引用,在调用目标方法前后添加额外逻辑-

代码示例

java
复制
下载
// 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");

执行结果:

text
复制
下载
before method send()
send message:java
after method send()

动态代理——JDK动态代理

定义:在程序运行时,运用Java反射机制动态创建代理类,无需手动编写代理类-

核心类Proxy + InvocationHandler-

代码示例

java
复制
下载
// 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

代码示例

java
复制
下载
// 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
与装饰器区别代理控制访问,装饰器增强功能

重点与易错点

  1. 静态代理:接口新增方法时,代理类必须同步修改,违背开闭原则

  2. JDK动态代理invoke()中必须调用method.invoke(target, args),否则目标逻辑不执行

  3. CGLIB代理:不能代理final修饰的类和方法

  4. 概念区分:代理模式的核心是“控制访问”,不是“增强行为”

进阶预告

下一篇将深入讲解装饰器模式,分析它与代理模式的异同以及在Java IO流中的应用,敬请期待。

文末互动:你在项目中用过哪种代理方式?遇到过哪些坑?欢迎在评论区留言交流。

猜你喜欢