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代理 继承目标类 无接口的类
相关推荐
2601_949816221 天前
Redis 配置日志
java
遇见你...1 天前
A01-Spring概述
java·后端·spring
紫金修道1 天前
【DeepAgent】概述
开发语言·数据库·python
Via_Neo1 天前
JAVA中以2为底的对数表示方式
java·开发语言
书到用时方恨少!1 天前
Python multiprocessing 使用指南:突破 GIL 束缚的并行计算利器
开发语言·python·并行·多进程
cch89181 天前
PHP五大后台框架横向对比
开发语言·php
天真萌泪1 天前
JS逆向自用
开发语言·javascript·ecmascript
野生技术架构师1 天前
一线大厂Java面试八股文全栈通关手册(含源码级详解)
java·开发语言·面试
廋到被风吹走1 天前
【AI】Codex 多语言实测:Python/Java/JS/SQL 效果横评
java·人工智能·python
Q一件事1 天前
R语言制图-相关性及关系网络图
开发语言·r语言