代理模式实现,静态,动态

一、代理模式核心概念

代理模式是一种结构型设计模式,核心思想是通过一个 "代理类" 来控制对 "目标类" 的访问,代理类可以在不修改目标类代码的前提下,为目标类添加额外功能(如日志、权限校验、性能监控等)。代理模式中主要包含 3 个角色:

  1. 目标对象(Real Subject):真正执行业务逻辑的类。
  2. 代理对象(Proxy):持有目标对象的引用,对外提供与目标对象相同的接口,在调用目标对象方法前后可以扩展逻辑。
  3. 抽象主题(Subject):目标对象和代理对象共同实现的接口(或继承的抽象类),定义核心业务方法。

二、静态代理

1. 定义

静态代理是指代理类在编译期就已经确定(由程序员手动编写代理类代码),代理类和目标类都实现同一个接口,代理类直接持有目标类的实例。

2. 代码示例

步骤 1:定义抽象主题接口(Subject)

java

运行

复制代码
// 抽象主题:订单服务接口
public interface OrderService {
    void createOrder(String orderId);
}

步骤 2:实现目标类(Real Subject)

java

运行

复制代码
// 目标类:真正处理订单的业务类
public class OrderServiceImpl implements OrderService {
    @Override
    public void createOrder(String orderId) {
        System.out.println("创建订单:" + orderId); // 核心业务逻辑
    }
}

步骤 3:编写静态代理类(Proxy)

java

运行

复制代码
// 静态代理类:扩展日志功能
public class OrderServiceProxy implements OrderService {
    // 持有目标对象引用
    private final OrderService target;

    // 通过构造器注入目标对象
    public OrderServiceProxy(OrderService target) {
        this.target = target;
    }

    @Override
    public void createOrder(String orderId) {
        // 前置增强:调用目标方法前添加日志
        System.out.println("【静态代理】创建订单前,记录日志:准备创建订单 " + orderId);
        // 调用目标对象的核心方法
        target.createOrder(orderId);
        // 后置增强:调用目标方法后添加逻辑
        System.out.println("【静态代理】创建订单后,记录日志:订单 " + orderId + " 创建成功");
    }
}

步骤 4:测试静态代理

java

运行

复制代码
public class StaticProxyTest {
    public static void main(String[] args) {
        // 1. 创建目标对象
        OrderService target = new OrderServiceImpl();
        // 2. 创建代理对象,传入目标对象
        OrderService proxy = new OrderServiceProxy(target);
        // 3. 通过代理对象调用方法(实际执行目标方法+扩展逻辑)
        proxy.createOrder("ORDER_001");
    }
}

输出结果

plaintext

复制代码
【静态代理】创建订单前,记录日志:准备创建订单 ORDER_001
创建订单:ORDER_001
【静态代理】创建订单后,记录日志:订单 ORDER_001 创建成功
3. 静态代理的特点
  • 优点:实现简单、逻辑清晰,不修改目标类代码即可扩展功能,符合 "开闭原则"。
  • 缺点
    1. 代理类与目标类一一对应,若有多个目标类需要代理,需编写大量代理类,代码冗余。
    2. 若抽象主题接口新增方法,目标类和所有代理类都需修改,维护成本高。

三、动态代理

1. 定义

动态代理是指代理类在运行期动态生成 (无需手动编写代理类代码),JDK 通过java.lang.reflect.ProxyInvocationHandler实现动态代理,核心是反射机制。

2. 核心 API

表格

类 / 接口 作用
InvocationHandler 回调接口,定义代理类的增强逻辑(invoke 方法)。
Proxy.newProxyInstance 静态方法,生成代理类实例,参数:类加载器、目标类实现的接口、InvocationHandler。
3. 代码示例(JDK 动态代理)

步骤 1:复用抽象主题和目标类(同静态代理的 OrderService、OrderServiceImpl)

步骤 2:编写 InvocationHandler 实现类(定义增强逻辑)

java

运行

复制代码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

// 动态代理的增强逻辑处理器
public class LogInvocationHandler implements InvocationHandler {
    // 持有目标对象(通用类型,适配所有目标类)
    private final Object target;

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

    /**
     * 代理类调用方法时,会回调此方法
     * @param proxy 代理对象本身(一般不用)
     * @param method 目标方法(被调用的方法)
     * @param args 目标方法的参数
     * @return 目标方法的返回值
     * @throws Throwable 目标方法抛出的异常
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 前置增强:日志记录
        System.out.println("【动态代理】调用方法前:" + method.getName() + ",参数:" + (args != null ? args[0] : ""));
        // 调用目标对象的核心方法
        Object result = method.invoke(target, args);
        // 后置增强:日志记录
        System.out.println("【动态代理】调用方法后:" + method.getName() + ",返回值:" + result);
        return result;
    }
}

步骤 3:测试动态代理

java

运行

复制代码
import java.lang.reflect.Proxy;

public class DynamicProxyTest {
    public static void main(String[] args) {
        // 1. 创建目标对象
        OrderService target = new OrderServiceImpl();
        // 2. 创建InvocationHandler,传入目标对象
        InvocationHandler handler = new LogInvocationHandler(target);
        // 3. 动态生成代理类实例
        OrderService proxy = (OrderService) Proxy.newProxyInstance(
                target.getClass().getClassLoader(), // 目标类的类加载器
                target.getClass().getInterfaces(),  // 目标类实现的接口(代理类会实现这些接口)
                handler                              // 增强逻辑处理器
        );
        // 4. 通过代理对象调用方法
        proxy.createOrder("ORDER_002");
    }
}

输出结果

plaintext

复制代码
【动态代理】调用方法前:createOrder,参数:ORDER_002
创建订单:ORDER_002
【动态代理】调用方法后:createOrder,返回值:null
4. 动态代理的特点
  • 优点
    1. 无需手动编写代理类,一个 InvocationHandler 可适配多个目标类,解决静态代理代码冗余问题。
    2. 接口新增方法时,无需修改代理相关代码,维护成本低。
  • 缺点
    1. JDK 动态代理只能代理实现了接口的类(无法代理普通类)。
    2. 基于反射实现,性能略低于静态代理(但日常开发中性能差异可忽略)。
5. 扩展:CGLIB 动态代理

若目标类未实现任何接口,可使用 CGLIB(Code Generation Library)实现动态代理(底层通过 ASM 字节码生成框架生成子类):

java

运行

复制代码
// 需引入CGLIB依赖(Maven)
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>

// CGLIB代理示例
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

// 目标类(无需实现接口)
public class UserService {
    public void addUser(String username) {
        System.out.println("添加用户:" + username);
    }
}

// CGLIB增强处理器
public class CglibLogInterceptor implements MethodInterceptor {
    private final Object target;

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

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        // 前置增强
        System.out.println("【CGLIB代理】调用方法前:" + method.getName());
        // 调用目标方法
        Object result = method.invoke(target, args);
        // 后置增强
        System.out.println("【CGLIB代理】调用方法后:" + method.getName());
        return result;
    }
}

// 测试CGLIB代理
public class CglibProxyTest {
    public static void main(String[] args) {
        UserService target = new UserService();
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(UserService.class); // 设置父类(目标类)
        enhancer.setCallback(new CglibLogInterceptor(target)); // 设置增强逻辑
        UserService proxy = (UserService) enhancer.create(); // 生成代理对象
        proxy.addUser("张三");
    }
}

四、静态代理 vs 动态代理对比

表格

维度 静态代理 动态代理(JDK)
代理类生成时机 编译期(手动编写) 运行期(反射动态生成)
代码冗余度 高(一个目标类对应一个代理类) 低(一个处理器适配多个目标类)
接口依赖 依赖(代理类和目标类实现同一接口) 依赖(仅能代理实现接口的类)
维护成本 高(接口变更需修改所有代理类) 低(接口变更无需修改代理逻辑)
性能 略高(直接调用) 略低(反射调用)
相关推荐
ydd1001006 天前
正则表达式匹配
正则表达式·代理模式
LayZhangStrive8 天前
设计模式(二)装饰器模式、代理模式(待更新)
设计模式·代理模式·装饰器模式·后端开发
Lam Tang12 天前
APS 系列文章10
java·代理模式
Lam Tang13 天前
APS 系列文章 08
java·代理模式
wangjialelele13 天前
动态规划DP经典题型总结(Java、C++):路径、子数组、子序列、背包问题详解
java·c++·算法·面试·动态规划·代理模式
聪明蛋子哟13 天前
从MCP到A2A再到Proxy:Agent-工具协议的三种代理模式
代理模式
Lam Tang13 天前
APS 系列文章 09
java·代理模式
Lam Tang14 天前
APS 系列文章 01
java·代理模式
只吃不吃香菜19 天前
Windows 下基于 YAML 规则的指定域名分流:以哔哩哔哩为例
网络·网络协议·计算机网络·网络安全·代理模式
zhongjunyao22 天前
Spec-Driven Development (SDD): The Definitive 2026 Guide
javascript·spring boot·python·eclipse·代理模式