编程自学指南:java程序设计开发,Java 代理模式(Proxy)详解,代理模式的核心思想:控制对象访问,静态代理与动态代理的实现方法,代理模式实现日志、事务等横切功能

编程自学指南:java程序设计开发,Java 代理模式(Proxy)详解

一、课程信息

学习目标

  1. 理解代理模式的核心思想:控制对象访问
  2. 掌握静态代理与动态代理的实现方法
  3. 能通过代理模式实现日志、事务等横切功能
  4. 区分代理模式与装饰器、适配器的差异

二、课程导入:生活中的代理

🌰 场景 1:租房中介

  • 租客(客户端)→ 中介(代理)→ 房东(真实对象)
  • 代理作用:过滤无效房源、收取中介费、协调双方

🌰 场景 2:明星经纪人

  • 活动方(客户端)→ 经纪人(代理)→ 明星(真实对象)
  • 代理作用:筛选活动、谈判价格、处理合同

三、核心概念与分类

1. 模式定义

为其他对象提供一种代理,以控制对原对象的访问。
核心:通过代理间接访问目标对象,添加额外逻辑

2. 核心角色

角色名称作用租房案例类比
抽象主题定义代理与真实对象的共同接口Rentable接口(有 rent () 方法)
真实主题实际业务逻辑的实现者Landlord类(房东)
代理主题代理对象,持有真实主题引用Agent类(中介)

3. 代理分类

🔨 静态代理
  • 代理类在编译时就已经存在
  • 需手动为每个真实主题创建代理类
🌀 动态代理
  • 代理类在运行时动态生成
  • 一个代理类可代理多个真实主题

四、静态代理实现

🔧 案例 1:租房中介(核心案例)

步骤 1:定义抽象主题
// 租房接口
interface Rentable {
    void rentHouse(); // 租房方法
}
步骤 2:实现真实主题
// 真实主题:房东
class Landlord implements Rentable {
    private String houseAddress;

    public Landlord(String houseAddress) {
        this.houseAddress = houseAddress;
    }

    @Override
    public void rentHouse() {
        System.out.println("出租房屋:" + houseAddress);
    }
}
步骤 3:实现代理主题
// 代理主题:中介
class Agent implements Rentable {
    private Rentable landlord; // 持有真实主题引用

    public Agent(Rentable landlord) {
        this.landlord = landlord;
    }

    @Override
    public void rentHouse() {
        beforeRent(); // 代理额外逻辑
        landlord.rentHouse(); // 调用真实主题方法
        afterRent(); // 代理额外逻辑
    }

    private void beforeRent() {
        System.out.println("中介带租客看房...");
    }

    private void afterRent() {
        System.out.println("中介收取5%中介费");
    }
}
步骤 4:客户端调用
public class RentClient {
    public static void main(String[] args) {
        Rentable landlord = new Landlord("阳光小区1栋");
        Rentable agent = new Agent(landlord); // 代理包装真实对象
        
        agent.rentHouse();
        // 输出:
        // 中介带租客看房...
        // 出租房屋:阳光小区1栋
        // 中介收取5%中介费
    }
}

五、动态代理(JDK 代理)

🔧 案例 2:日志代理(核心案例)

步骤 1:定义业务接口
interface Calculator {
    int add(int a, int b);
    int subtract(int a, int b);
}
步骤 2:实现真实主题
class RealCalculator implements Calculator {
    @Override
    public int add(int a, int b) {
        return a + b;
    }

    @Override
    public int subtract(int a, int b) {
        return a - b;
    }
}
步骤 3:实现 InvocationHandler
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

class LogProxy implements InvocationHandler {
    private Object target; // 被代理的目标对象

    public LogProxy(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 调用前记录日志
        System.out.println("调用方法:" + method.getName() + ",参数:" + Arrays.toString(args));
        
        // 调用真实方法
        Object result = method.invoke(target, args);
        
        // 调用后记录日志
        System.out.println("方法返回:" + result);
        
        return result;
    }
}
步骤 4:生成代理对象
import java.lang.reflect.Proxy;

public class DynamicProxyDemo {
    public static void main(String[] args) {
        Calculator realCalc = new RealCalculator();
        
        // 创建InvocationHandler
        InvocationHandler handler = new LogProxy(realCalc);
        
        // 动态生成代理对象
        Calculator proxy = (Calculator) Proxy.newProxyInstance(
            Calculator.class.getClassLoader(),
            new Class<?>[] {Calculator.class},
            handler
        );
        
        int sum = proxy.add(3, 5); // 调用代理方法
    }
}

六、代理模式 vs 装饰器模式

特性代理模式装饰器模式
核心目的控制对原对象的访问扩展原对象的功能
调用方式代理→原对象(间接调用)装饰器→原对象(增强调用)
透明性客户端感知不到代理的存在客户端知道装饰器的存在

七、JDK 代理 vs CGLIB 代理

特性JDK 代理CGLIB 代理
依赖必须有接口无需接口,通过继承实现
性能较高(基于接口反射)较低(基于继承和字节码生成)
常用场景Spring AOP(有接口时)Spring AOP(无接口时)

八、适用场景与优缺点

✅ 优点

  1. 解耦:客户端与真实主题解耦
  2. 扩展性:方便添加日志、事务等通用功能
  3. 保护:控制对敏感资源的访问

⚠️ 缺点

  • 静态代理:类数量爆炸(每个接口对应一个代理类)
  • 动态代理:对原始类有侵入性(需实现接口)

🔥 适用场景

  1. 日志记录:记录方法调用信息
  2. 事务管理:在方法前后开启 / 提交事务
  3. 远程代理:调用远程服务(如 RPC 框架)

九、课堂练习

练习 1:实现事务代理

需求

  • UserService接口添加事务代理
  • 方法执行前输出 "开启事务",执行后输出 "提交事务"

练习 2:修复代理错误

错误代码

Proxy.newProxyInstance(
    RealCalculator.class.getClassLoader(),
    new Class<?>[] {RealCalculator.class}, // 错误:应为接口
    handler
);

十、课程总结

知识图谱:

plaintext

代理模式  
   ↳ 核心:控制访问,中间层添加逻辑  
   ↳ 分类:静态代理(编译时)、动态代理(运行时)  
   ↳ 动态代理核心:InvocationHandler、Proxy.newProxyInstance  
   ↳ 应用:日志、事务、远程调用  

口诀记忆:

“代理模式像中介,访问对象要经过,
静态代理手动写,动态代理自动造,
日志事务加中间,系统解耦真奇妙!”

十一、课后作业

必做 1:实现缓存代理

需求

  • DataService接口的getData(String key)方法添加缓存
  • 首次调用时查询数据库,后续调用直接从缓存获取

必做 2:分析 Spring AOP 代理

任务

  1. 配置 Spring AOP,为UserService添加日志切面
  2. 说明使用的是 JDK 代理还是 CGLIB 代理
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zl515035644

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值