Java 代理模式(简单理解回顾)

✅ 一句话定义

代理 = 找一个替身(代理类),帮你控制对真实目标对象的访问;在调用真实方法前后,加额外逻辑,不改原有目标代码。

真实对象:做核心业务 代理对象:增强(日志、耗时、权限、事务),不改动原有业务代码

✅ 为什么会出现代理模式(解决什么痛点)

核心痛点:开闭原则

原有业务代码只负责核心逻辑,不想修改它;但又想在方法执行前后加通用能力(日志、监控、权限校验、事务)。

如果不使用代理: 直接改业务方法,到处写日志代码 → 业务代码和通用杂糅在一起;新增 / 删除日志,要修改所有业务方法,违反开闭原则。

代理思路:通用增强逻辑放到代理里面,业务代码不动。

✅ 3 种代理分类(只抓重点)

1. 静态代理(写死代码,编译期就写好代理类)

特点:代理类代码手动写,一个目标类对应一个代理类 缺点:目标类越多,代理类爆炸,维护麻烦。

示例场景:用户服务,新增打印耗时

java 复制代码
// 接口
public interface UserService {
    void addUser();
}
// 真实类(核心业务)
class UserServiceImpl implements UserService{
    @Override
    public void addUser() {
        System.out.println("新增用户【核心业务】");
    }
}
// 静态代理类
public class UserServiceProxy implements UserService{
    // 持有真实对象
    private UserService target;
    public UserServiceProxy(UserService target){
        this.target = target;
    }
    @Override
    public void addUser() {
        System.out.println("【代理前置】方法开始,记录时间");
        target.addUser(); // 调用真实业务
        System.out.println("【代理后置】方法结束,统计耗时");
    }
}

使用:

java 复制代码
UserService target = new UserServiceImpl();
UserService proxy = new UserServiceProxy(target);
proxy.addUser();

2. JDK 动态代理(⭐Spring AOP 底层之一)

要求:目标对象必须实现接口 运行期动态生成代理类,不用手写代理代码。 原理:java.lang.reflect.Proxy + InvocationHandler 调用代理方法时,进入invoke(),在 invoke 里面执行增强 + 真实方法。

3. CGLIB 代理(⭐Spring AOP 底层之二)

目标类不用实现接口,通过继承目标类生成子类作为代理 不能代理 final 类 /final 方法(无法继承重写)
Spring AOP 规则: 目标有接口 → 默认 JDK 动态代理;无接口 → CGLIB

✅ 日常开发什么时候巧妙用代理(真实场景)

  1. Spring AOP(最常见!本质就是动态代理) 给接口方法统一加:日志、耗时统计、异常捕获、权限校验、事务 @Transactional

事务:方法执行前开启事务,异常回滚,正常提交,全部在代理增强逻辑,业务代码不用管。

  1. RPC 框架:Dubbo 消费端拿到的服务接口,其实是代理对象。调用接口方法时,代理帮你做序列化、网络传输、调用远程服务,本地只是接口,真实逻辑在远端。

  2. Mybatis Mapper 接口 Mapper 只是接口,没有实现类!Mybatis 启动时生成代理对象。调用 mapper.select (),代理帮你拼装 SQL、执行 JDBC、封装结果。👉 这个是面试高频!

  3. 本地缓存、限流、熔断增强 调用目标方法前,代理先判断限流;命中缓存直接返回,不执行真实方法。

✅ 代理 vs 装饰器(容易混淆,一句话区分)

  • 代理:侧重【控制访问】 ,能不能执行、权限、远程调用;代理和目标不一定实现一模一样的目的(Dubbo 代理根本本地没有实现)
  • 装饰器:侧重【增强功能】,包装同一个对象,不断叠加能力;IO 流 BufferedReader 就是装饰器

✅ 核心总结

  1. 目的:不修改原有业务代码,对方法做增强,遵守开闭原则
  2. 静态代理:手写代理类,类多了臃肿;
  3. JDK 代理:需要接口;CGLIB:继承,不需要接口
  4. 项目最常见:SpringAOP、Dubbo 消费端、MyBatis Mapper,底层都是动态代理

📝 速记口诀

代理替身控访问,原有业务不用改; 静态手写类爆炸,动态运行生成快; JDK 代理要接口,CGLIB 继承来覆盖; AOP 事务与 Mybatis,RPC 远程都靠它。

相关推荐
liangbo75 小时前
Spring系列03:Bean 的三种注册方式
java·spring
余槐i5 小时前
MySQL 8.0 联合索引走错:为什么加了索引查询反而从 200ms 变成 8s
java·spring boot·mysql·docker·性能优化
SL_staff5 小时前
制造业订单交付总在最后一刻爆雷?用结构化计划模板打通跨系统协同断点
java·开源·全栈
Dovis(誓平步青云)5 小时前
启元 T1 Pro 人机相声搭档:语音-触摸双通道的启元机器人节奏引擎设计
开发语言·前端·javascript·microsoft·交互
做科研的周师兄5 小时前
【R语言实战】MixSIAR 氢氧同位素水源解析全流程:从混合空间校验到贝叶斯 MCMC 收敛诊断(后附完整代码)
java·大数据·开发语言
有点小困9775 小时前
Spring Boot 异步任务 ClassLoader 踩坑实录:ClassNotFoundException 的诡异真相
java
aiks_yuan5 小时前
Java 正则表达式很慢怎么办:Pattern 预编译只快 2~5 倍,真正卡死线程的是回溯(JDK 8、11、17 实测)
java
用户3721574261356 小时前
C# 填写 PDF 交互式表单:文本框、复选框、单选按钮和下拉列表
java
IT枫斗者枫哥6 小时前
Java 退款请求超时,先查原单还是重试?
java·后端
特立独行的猫A6 小时前
我用仓颉写了个多平台发文工具:一份 Markdown,一条命令发六个平台
java·后端·python