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

一、代理模式核心概念

代理模式是一种结构型设计模式,核心思想是通过一个 "代理类" 来控制对 "目标类" 的访问,代理类可以在不修改目标类代码的前提下,为目标类添加额外功能(如日志、权限校验、性能监控等)。代理模式中主要包含 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)
代理类生成时机 编译期(手动编写) 运行期(反射动态生成)
代码冗余度 高(一个目标类对应一个代理类) 低(一个处理器适配多个目标类)
接口依赖 依赖(代理类和目标类实现同一接口) 依赖(仅能代理实现接口的类)
维护成本 高(接口变更需修改所有代理类) 低(接口变更无需修改代理逻辑)
性能 略高(直接调用) 略低(反射调用)
相关推荐
wuqingshun3141593 天前
什么是代理模式?一般用在什么场景?
java·mysql·代理模式
我登哥MVP3 天前
走进 Gang of Four 设计模式:代理模式
设计模式·代理模式
折哥的程序人生 · 物流技术专研5 天前
Java 23 种设计模式:从踩坑到精通 | 番外:代理 vs 装饰器 —— Spring AOP 到底用了哪个?
java·代理模式·装饰器模式·java面试·java设计模式·springaop·从踩坑到精通
初学者,亦行者6 天前
算法设计与分析3:贪心法 - 求解最短路径问题(TSP)
算法·代理模式
2501_916007479 天前
iPad 怎么抓包 从代理模式到暴力抓包的操作方法
android·ios·小程序·uni-app·代理模式·iphone·ipad
越甲八千9 天前
代理模式和适配器模式区别
代理模式·适配器模式
AI人工智能+电脑小能手13 天前
【大白话说Java面试题 第156题】【06_Spring篇】第16题:Spring 用到了哪些设计模式?
java·spring·设计模式·代理模式·工厂模式
许彰午1 个月前
36_Java设计模式之代理模式
java·设计模式·代理模式
devilnumber1 个月前
静态代理 & 动态代理:实战运用 + 场景区别 + 怎么选
java·开发语言·代理模式
devilnumber1 个月前
想真正吃透 + 灵活运用 Java 代理模式
java·开发语言·代理模式