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代理 继承目标类 无接口的类
相关推荐
FinelyYang1 小时前
考试系统简答题 AI 阅卷:覆盖度驱动、异步评分与人机协同落地实践
java·大数据·人工智能
天外飞雨道沧桑1 小时前
我在浏览器里练了一个 792 参数的大模型,它学会了补全 JavaScript
开发语言·javascript·ai·ecmascript·ai编程
hhhjhl1 小时前
Yup 跨端三方库仓颉适配全流程
java·开发语言
运维行者_1 小时前
网络监控工具怎么选?从设备监控到智能运维的能力清单
运维·服务器·开发语言·网络·人工智能·支持向量机·php
用户3126874877201 小时前
Java 对象内存布局:压缩指针、对齐填充与 JOL 实测
java
东华果汁哥1 小时前
【Spring AI 实战】从零开始调用大模型接口,构建 Java AI 应用
java·人工智能·spring
“AI国潮设计-小江”2 小时前
【AIGC实战】Python+SDXL打造“潮汕英歌舞IP”万能立牌插件,批量生成“发财元宝蛋糕”视觉营销方案(附源码)
开发语言·人工智能·python·prompt·aigc
Song_RsHt2 小时前
Redis 与微服务,什么场景下无可替代
java·redis·后端·学习·微服务
学长毕设2 小时前
基于SpringBoot的建筑材料管理系统-附源码
java·spring boot·后端
lhbweilai2 小时前
分析FreeRTOS内核对象API的源码实现(八)
c语言·开发语言·stm32·单片机