Java基础——代理

1. 什么是代理?

代理就是替身、经纪人。

生活例子:

明星经纪人:明星不直接见粉丝,由经纪人代理

房产中介:房东不直接卖房,由中介代理

在程序中:

不直接调用目标对象,而是通过代理对象调用可以在调用前后添加额外操作(权限校验、日志记录等)

2. 静态代理

编译时就确定代理类和目标类的关系

java 复制代码
// 1. 定义接口
interface IPerson {
    void findLove();  // 找对象
    void findJob();   // 找工作
}
// 2. 目标类(明星本人)
class Star implements IPerson {
    @Override
    public void findLove() {
        System.out.println("明星要求:肤白貌美大长腿");
    }
    
    @Override
    public void findJob() {
        System.out.println("明星要求:日薪208万");
    }
}
// 3. 代理类(经纪人)
class Agent implements IPerson {
    private Star star;  // 持有目标对象的引用
    
    public Agent(Star star) {
        this.star = star;
    }
    
    @Override
    public void findLove() {
        System.out.println("经纪人筛选合适的人选");  // 前置操作
        star.findLove();  // 实际调用目标
        System.out.println("经纪人安排约会");      // 后置操作
    }
    
    @Override
    public void findJob() {
        System.out.println("经纪人洽谈合作");
        star.findJob();
        System.out.println("经纪人签约");
    }
}
// 4. 使用
public class Test {
    public static void main(String[] args) {
        Star star = new Star();
        Agent agent = new Agent(star);
        agent.findLove();  // 通过代理调用
    }
}

静态代理的优缺点

每个目标类都需要写一个代理类,代码冗余,接口修改,代理类也要改

3. 动态代理

程序运行时动态生成代理类

(1)JDK动态代理(基于接口): 核心类是**java.lang.reflect.Proxy**

java 复制代码
// 1. 定义接口
interface IPerson {
    void findLove();
    void findJob();
}
// 2. 目标类
class Star implements IPerson {
    @Override
    public void findLove() {
        System.out.println("明星要求:肤白貌美大长腿");
    }
    
    @Override
    public void findJob() {
        System.out.println("明星要求:日薪208万");
    }
}
// 3. 使用JDK动态代理
public class Test {
    public static void main(String[] args) {
        Star star = new Star();
        
        // 创建代理对象
        IPerson proxy = (IPerson) Proxy.newProxyInstance(
            Star.class.getClassLoader(),  // 类加载器
            Star.class.getInterfaces(),  // 接口数组
            new InvocationHandler() {     // 调用处理器
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) {
                    // proxy: 代理对象
                    // method: 正在调用的方法
                    // args: 方法参数
                    
                    System.out.println("前置处理:" + method.getName());
                    
                    // 调用目标方法
                    Object result = method.invoke(star, args);
                    
                    System.out.println("后置处理");
                    return result;
                }
            }
        );
        
        // 通过代理调用
        proxy.findLove();
    }
}

执行流程:

JDK动态代理原理图

(2)CGLIB动态代理(基于继承)

java 复制代码
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
class Star {
    public void findLove() {
        System.out.println("明星要求:肤白貌美大长腿");
    }
}
public class Test {
    public static void main(String[] args) {
        Star star = new Star();
        
        // 创建代理
        Star proxy = (Star) Enhancer.create(
            Star.class,              // 目标类
            new MethodInterceptor() {  // 回调
                @Override
                public Object intercept(Object obj, Method method, 
                        Object[] args, MethodProxy proxy) {
                    System.out.println("前置处理");
                    Object result = method.invoke(star, args);
                    System.out.println("后置处理");
                    return result;
                }
            }
        );
        
        proxy.findLove();
    }
}

CGLIB原理:

继承目标类,重写方法

通过superclass调用父类方法

(3)JDK动态代理 vs CGLIB

特性 JDK动态代理 CGLIB
原理 基于接口(实现接口) 基于继承(继承目标类
要求 目标类必须实现接口 目标类可以没有接口
性能 较快(直接生成类) 较慢(生成子类)
JDK自带 ✓ 是 ✗ 需要导包
无法代理 final类、private方法 final类、final方法

4. 代理的实际应用

(1)AOP(面向切面编程)

java 复制代码
@Aspect  // 切面类
class LogAspect {
    @Before("execution(* com.qcby.service.*.*(..))")
    public void before() {
        System.out.println("方法执行前记录日志");
    }
    
    @After("execution(* com.qcby.service.*.*(..))")
    public void after() {
        System.out.println("方法执行后记录日志");
    }
}

(2)事务管理

java 复制代码
public class TransactionProxy {
    public static <T> T getProxy(T target) {
        return (T) Proxy.newProxyInstance(
            target.getClass().getClassLoader(),
            target.getClass().getInterfaces(),
            (proxy, method, args) -> {
                try {
                    System.out.println("开启事务");
                    Object result = method.invoke(target, args);
                    System.out.println("提交事务");
                    return result;
                } catch (Exception e) {
                    System.out.println("回滚事务");
                    throw e;
                }
            }
        );
    }
}
// 使用
UserService userService = new UserServiceImpl();
UserService proxy = TransactionProxy.getProxy(userService);
proxy.save(user);  // 自动开启/提交事务

(3)远程调用(RPC)

Dubbo、Feign等框架用代理模拟远程调用:

  • 客户端调用代理方法

  • 代理将调用封装成网络请求

  • 服务器执行并返回结果

5. 代理的对比

代理类型 实现方式 适用场景
静态代理 手动写代理类 简单场景
JDK动态代理 实现接口,运行时生成 有接口的类
CGLIB代理 继承目标类 无接口的类
相关推荐
Scott9999HH4 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
腻害兔4 小时前
【若依项目-产品经理视角】深度拆解 RuoYi-Vue-Pro 商城模块:从商品管理到交易引擎,50 张表撑起一整套电商系统
java·大数据·vue.js·产品经理·ai编程
码智社5 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海5 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖6 小时前
JDK 26 新特性详解
java·开发语言
马优晨6 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区8 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大9 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai9 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
维天说9 小时前
CLI-Switch 2026年3月版历史设计:Hook、TTY 隔离与 JSON 状态
java·服务器·json