动态代理实现原理分析
字数 1746 2025-08-23 18:31:08

动态代理实现原理详解

一、代理模式概述

代理是一种常用的设计模式,其目的是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息、过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。

类比:可以理解为朋友圈中的代购,在买家与卖家间进行协调。

二、静态代理

1. 静态代理实现方式

要求被代理类和代理类同时实现相应的一套接口,通过代理类调用重写接口的方法,实际上调用的是原始对象的同样方法。

实例:电影公司委托影院播放电影,影院可以在播放前后添加自己的业务逻辑(如卖爆米花、饮料等)。

2. 静态代理代码实现

// 定义接口
public interface Movie {
    public void show();
}

// 委托类
class Real implements Movie {
    public void show() {
        System.out.println("您正在观看电影");
    }
}

// 代理类
public class StaticPoxy implements Movie {
    private Movie movie;
    
    public StaticPoxy(Movie movie) {
        this.movie = movie;
    }
    
    @Override
    public void show() {
        System.out.println("电影马上开始了,爆米花、饮料快来买啊");
        movie.show();
        System.out.println("电影已经结束了,爆米花、饮料买回家吧");
    }
}

// 测试类
public class Test {
    public static void main(String[] args) {
        Movie test = new Real();
        System.out.println("-----无代理-----");
        test.show();
        System.out.println("-----静态代理-----");
        StaticPoxy staticPoxy = new StaticPoxy(test);
        staticPoxy.show();
    }
}

3. 静态代理优缺点

优点

  • 可以在不修改委托类源代码的情况下,通过代理类扩展功能

缺点

  1. 代理类和委托类都实现相同接口,导致代码重复
  2. 添加新接口时,代理类和委托类都需要修改
  3. 产生冗余代码,增加运维成本

三、动态代理

1. 动态代理概述

动态代理是指在内存中动态构建代理对象(需要指定要代理的目标对象实现的接口类型),利用JDK的API生成指定接口的对象,也称为JDK代理或接口代理。

主要涉及java.lang.reflect包下的:

  • Proxy
  • InvocationHandler接口

2. 核心类与接口

(1) Proxy类

Proxy类继承了java.io.Serializable接口,其中最重要的方法是:

public static Object newProxyInstance(
    ClassLoader loader, 
    Class<?>[] interfaces, 
    InvocationHandler h
)

参数说明

  • loader:类加载器
  • interfaces:要代理的接口数组
  • h:InvocationHandler对象

(2) InvocationHandler接口

每个proxy代理实例都有一个关联的调用处理程序,当代理实例调用方法时,方法调用会被编码分派到调用处理程序的invoke方法。

public interface InvocationHandler {
    public Object invoke(
        Object proxy, 
        Method method, 
        Object[] args
    ) throws Throwable;
}

参数说明

  • proxy:代理对象
  • method:要调用的代理对象方法
  • args:方法调用参数

3. 动态代理实现示例

(1) 定义接口和实现类

// 卖酒接口
public interface SellWine {
    public void sell();
}

// 卖酒实现类
public class Wine implements SellWine {
    public void sell() {
        System.out.println("卖酒");
    }
}

(2) 实现InvocationHandler

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class DynamicPoxy implements InvocationHandler {
    private Object agent;
    
    public DynamicPoxy(Object poxy) {
        this.agent = poxy;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("销售开始 代理商是:" + this.getClass().getSimpleName());
        method.invoke(agent, args);
        System.out.println("销售结束");
        return null;
    }
}

(3) 测试类

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class Test {
    public static void main(String[] args) {
        Wine wine = new Wine();
        InvocationHandler invocationHandler = new DynamicPoxy(wine);
        
        SellWine dynamicPoxy1 = (SellWine) Proxy.newProxyInstance(
            wine.getClass().getClassLoader(),
            wine.getClass().getInterfaces(),
            invocationHandler
        );
        
        dynamicPoxy1.sell();
    }
}

4. 动态代理原理分析

(1) 代理文件生成

在测试类中添加以下代码可生成代理类文件:

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

代理类生成流程:

  1. 调用Proxy.newProxyInstance()
  2. 进入getProxyClass0()方法
  3. 执行proxyClassCache.get(loader, interfaces)
  4. 在第二轮循环中调用get()方法
  5. 调用ProxyClassFactoryapply()方法
  6. 通过defineClass0()生成class对象

ProxyClassFactory的作用:

  • 生成新的代理类的class对象
  • BiFunction接口的实现类
  • apply()方法中:
    • 检查传入的class对象数组
    • 生成新的代理类的类名proxyName
    • 调用defineClass0方法生成代理类class对象

(2) 方法调用流程

  1. 调用dynamicPoxy1.sell()时,会调用$Proxy0.class中的sell()
  2. $Proxy0.sell()中会调用h.invoke()
  3. h是在测试类中创建的DynamicPoxy对象
  4. 因此实际调用的是DynamicPoxy.invoke()
  5. 通过method.invoke(agent, args)调用原始对象的sell()方法

5. 动态代理扩展性

动态代理的优势在于可以轻松扩展新功能而无需修改代理类。

示例:添加卖烟功能

// 卖烟接口
public interface SellCigarette {
    public void sell();
}

// 卖烟实现类
public class Cigarette implements SellCigarette {
    public void sell() {
        System.out.println("卖烟");
    }
}

// 测试类扩展
public class Test {
    public static void main(String[] args) {
        Wine wine = new Wine();
        Cigarette cigarette = new Cigarette();
        
        InvocationHandler invocationHandler1 = new DynamicPoxy(wine);
        InvocationHandler invocationHandler2 = new DynamicPoxy(cigarette);
        
        SellWine dynamicPoxy1 = (SellWine) Proxy.newProxyInstance(
            wine.getClass().getClassLoader(),
            wine.getClass().getInterfaces(),
            invocationHandler1
        );
        
        SellCigarette dynamicPoxy2 = (SellCigarette) Proxy.newProxyInstance(
            cigarette.getClass().getClassLoader(),
            cigarette.getClass().getInterfaces(),
            invocationHandler2
        );
        
        dynamicPoxy1.sell();
        System.out.println("----------------");
        dynamicPoxy2.sell();
    }
}

四、总结对比

特性 静态代理 动态代理
实现方式 手动编写代理类 运行时动态生成
代码冗余 高(需实现相同接口) 低(通用InvocationHandler)
扩展性 差(新增接口需修改代码) 好(只需新增接口和实现类)
维护成本
性能 略高(直接调用) 略低(反射调用)

动态代理通过Java反射机制在运行时动态创建代理对象,避免了静态代理的代码冗余问题,大大提高了系统的灵活性和可扩展性。

动态代理实现原理详解 一、代理模式概述 代理是一种常用的设计模式,其目的是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息、过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。 类比:可以理解为朋友圈中的代购,在买家与卖家间进行协调。 二、静态代理 1. 静态代理实现方式 要求被代理类和代理类同时实现相应的一套接口,通过代理类调用重写接口的方法,实际上调用的是原始对象的同样方法。 实例 :电影公司委托影院播放电影,影院可以在播放前后添加自己的业务逻辑(如卖爆米花、饮料等)。 2. 静态代理代码实现 3. 静态代理优缺点 优点 : 可以在不修改委托类源代码的情况下,通过代理类扩展功能 缺点 : 代理类和委托类都实现相同接口,导致代码重复 添加新接口时,代理类和委托类都需要修改 产生冗余代码,增加运维成本 三、动态代理 1. 动态代理概述 动态代理是指在内存中动态构建代理对象(需要指定要代理的目标对象实现的接口类型),利用JDK的API生成指定接口的对象,也称为JDK代理或接口代理。 主要涉及 java.lang.reflect 包下的: Proxy 类 InvocationHandler 接口 2. 核心类与接口 (1) Proxy类 Proxy 类继承了 java.io.Serializable 接口,其中最重要的方法是: 参数说明 : loader :类加载器 interfaces :要代理的接口数组 h :InvocationHandler对象 (2) InvocationHandler接口 每个proxy代理实例都有一个关联的调用处理程序,当代理实例调用方法时,方法调用会被编码分派到调用处理程序的 invoke 方法。 参数说明 : proxy :代理对象 method :要调用的代理对象方法 args :方法调用参数 3. 动态代理实现示例 (1) 定义接口和实现类 (2) 实现InvocationHandler (3) 测试类 4. 动态代理原理分析 (1) 代理文件生成 在测试类中添加以下代码可生成代理类文件: 代理类生成流程: 调用 Proxy.newProxyInstance() 进入 getProxyClass0() 方法 执行 proxyClassCache.get(loader, interfaces) 在第二轮循环中调用 get() 方法 调用 ProxyClassFactory 的 apply() 方法 通过 defineClass0() 生成class对象 ProxyClassFactory 的作用: 生成新的代理类的class对象 是 BiFunction 接口的实现类 在 apply() 方法中: 检查传入的class对象数组 生成新的代理类的类名 proxyName 调用 defineClass0 方法生成代理类class对象 (2) 方法调用流程 调用 dynamicPoxy1.sell() 时,会调用 $Proxy0.class 中的 sell() $Proxy0.sell() 中会调用 h.invoke() h 是在测试类中创建的 DynamicPoxy 对象 因此实际调用的是 DynamicPoxy.invoke() 通过 method.invoke(agent, args) 调用原始对象的 sell() 方法 5. 动态代理扩展性 动态代理的优势在于可以轻松扩展新功能而无需修改代理类。 示例 :添加卖烟功能 四、总结对比 | 特性 | 静态代理 | 动态代理 | |------|---------|---------| | 实现方式 | 手动编写代理类 | 运行时动态生成 | | 代码冗余 | 高(需实现相同接口) | 低(通用InvocationHandler) | | 扩展性 | 差(新增接口需修改代码) | 好(只需新增接口和实现类) | | 维护成本 | 高 | 低 | | 性能 | 略高(直接调用) | 略低(反射调用) | 动态代理通过Java反射机制在运行时动态创建代理对象,避免了静态代理的代码冗余问题,大大提高了系统的灵活性和可扩展性。