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代理 继承目标类 无接口的类
相关推荐
赵谨言1 小时前
基于YOLOv5的植物目标检测研究
大数据·开发语言·经验分享·python
野生技术架构师1 小时前
互联网大厂必备 Java 面试八股文真题解析
java·开发语言·面试
Rsun045511 小时前
synchronized关键字的底层实现
java
不光头强1 小时前
IO流知识点
开发语言·python
老约家的可汗1 小时前
C++篇之类和对象下
java·开发语言·c++
水月wwww1 小时前
Rust的安装与卸载 | windows
开发语言·windows·rust
SouthRosefinch1 小时前
一、HTML简介与开发环境
开发语言·前端·html
€8112 小时前
Java入门级教程27——ActiveMQ的下载与应用
java·开发语言·activemq·点对点文本消息发送·点对点对象消息发送·mysql+redis·序列化对象消息传输
Irissgwe2 小时前
C&C++内存管理
c语言·开发语言·c++·c++内存管理