基于代理模式:深入了解静态代理和动态代理

发布于:2025-09-11 ⋅ 阅读:(15) ⋅ 点赞:(0)


前言

        代理模式是设计模式中结构型模式的一种,它的核心思想是:通过一个 “代理对象” 来间接访问 “目标对象”,并在访问前后添加额外操作,从而实现对目标对象的控制或功能增强。今天我们从代理模式开始了解,基于代理模式去深入了解静态代理模式和动态代理模式。


一、为什么需要代理模式?

想象一个场景:你想租房,但不想直接和房东打交道(比如需要筛选房源、签订合同、处理售后等),这时可以找中介 —— 中介就是 “代理”,它帮你对接房东(目标对象),同时还能提供额外服务(如房源筛选、合同审核)。

在程序中,代理模式的核心价值在于:

  • 控制访问:比如限制某些用户访问敏感方法;
  • 功能增强:在不修改目标对象代码的前提下,添加日志、事务、缓存等通用逻辑(符合 “开闭原则”);
  • 隔离复杂性:比如远程代理(RPC)中,代理对象封装网络通信细节,让调用者像调用本地方法一样使用远程服务。

二、代理模式的核心角色

代理模式包含 3 个核心角色,缺一不可:

角色 定义 举例
抽象主题(Subject) 定义目标对象和代理对象的共同接口,确保代理对象可以替代目标对象。 UserService 接口
真实主题(Real Subject) 被代理的对象,包含核心业务逻辑。 UserServiceImpl(实现UserService
代理主题(Proxy) 实现抽象主题接口,内部持有真实主题的引用,在调用真实主题方法前后添加增强逻辑。 UserServiceProxy(实现UserService

三、静态代理:手动编写代理类

静态代理是最简单的代理实现方式:代理类在编译期手动编写完成,与目标类实现相同的接口。

案例:给用户服务添加日志记录

假设我们有一个 “用户服务”,需要在调用其方法时自动记录日志(调用时间、方法名等),但不想修改原有服务的代码。

1. 定义抽象主题(接口)
// 抽象主题:定义用户服务的接口
public interface UserService {
    void addUser(String username); // 添加用户
    void deleteUser(String username); // 删除用户
}
2. 实现真实主题(目标类)
// 真实主题:包含核心业务逻辑
public class UserServiceImpl implements UserService {
    @Override
    public void addUser(String username) {
        // 核心业务:添加用户
        System.out.println("【核心业务】添加用户:" + username);
    }

    @Override
    public void deleteUser(String username) {
        // 核心业务:删除用户
        System.out.println("【核心业务】删除用户:" + username);
    }
}
3. 实现代理主题(代理类)
import java.util.Date;

// 代理主题:增强目标类的方法
public class UserServiceProxy implements UserService {
    // 持有真实主题的引用(通过构造器传入)
    private UserService target;

    public UserServiceProxy(UserService target) {
        this.target = target;
    }

    @Override
    public void addUser(String username) {
        // 增强逻辑:方法调用前记录日志
        System.out.println("===== 日志开始 =====");
        System.out.println("时间:" + new Date());
        System.out.println("调用方法:addUser,参数:" + username);

        // 调用真实主题的核心业务
        target.addUser(username);

        // 增强逻辑:方法调用后记录日志
        System.out.println("方法执行完成");
        System.out.println("===== 日志结束 =====");
    }

    @Override
    public void deleteUser(String username) {
        // 同样添加增强逻辑
        System.out.println("===== 日志开始 =====");
        System.out.println("时间:" + new Date());
        System.out.println("调用方法:deleteUser,参数:" + username);

        target.deleteUser(username);

        System.out.println("方法执行完成");
        System.out.println("===== 日志结束 =====");
    }
}
4. 使用代理对象
public class Main {
    public static void main(String[] args) {
        // 1. 创建真实主题对象(目标对象)
        UserService userService = new UserServiceImpl();

        // 2. 创建代理对象(传入目标对象)
        UserService proxy = new UserServiceProxy(userService);

        // 3. 调用代理对象的方法(实际会触发增强逻辑 + 核心业务)
        proxy.addUser("张三");
        System.out.println("-----");
        proxy.deleteUser("李四");
    }
}
运行结果
===== 日志开始 =====
时间:Wed Sep 10 16:30:00 CST 2025
调用方法:addUser,参数:张三
【核心业务】添加用户:张三
方法执行完成
===== 日志结束 =====
-----
===== 日志开始 =====
时间:Wed Sep 10 16:30:00 CST 2025
调用方法:deleteUser,参数:李四
【核心业务】删除用户:李四
方法执行完成
===== 日志结束 =====
静态代理的优缺点
  • 优点:简单直观,逻辑清晰,无需依赖框架;
  • 缺点
    1. 代码冗余:每一个目标类都需要对应一个代理类(如果有 100 个目标类,就需要 100 个代理类);
    2. 维护成本高:当接口新增 / 修改方法时,所有代理类都需要同步修改(违反 “开闭原则”)。

四、动态代理:运行时自动生成代理类

为了解决静态代理的缺点,动态代理应运而生:代理类在程序运行时动态生成(无需手动编写),同一个代理逻辑可以复用在多个目标类上。

Java 中常见的动态代理实现有两种:

1. JDK 动态代理(基于接口)

JDK 动态代理是 Java 原生支持的代理方式,核心依赖java.lang.reflect.Proxyjava.lang.reflect.InvocationHandler要求目标类必须实现接口

核心原理:
  • 运行时,Proxy类根据目标类的接口动态生成代理类(类名通常为$ProxyN);
  • 所有对代理对象的方法调用,都会被转发到InvocationHandlerinvoke()方法;
  • invoke()方法中统一编写增强逻辑(如日志),再通过反射调用目标方法。
代码示例(复用上面的UserService):
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Date;

// 1. 定义增强逻辑处理器(实现InvocationHandler)
class LogInvocationHandler implements InvocationHandler {
    private Object target; // 目标对象

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

    // 所有代理方法调用都会触发此方法
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 增强逻辑:前置处理
        System.out.println("===== 日志开始 =====");
        System.out.println("时间:" + new Date());
        System.out.println("调用方法:" + method.getName());
        System.out.println("参数:" + (args != null ? args[0] : "无"));

        // 调用目标对象的方法(核心业务)
        Object result = method.invoke(target, args);

        // 增强逻辑:后置处理
        System.out.println("方法执行完成");
        System.out.println("===== 日志结束 =====");

        return result;
    }
}

// 2. 使用动态代理
public class DynamicProxyDemo {
    public static void main(String[] args) {
        // 目标对象
        UserService target = new UserServiceImpl();

        // 生成代理对象
        UserService proxy = (UserService) Proxy.newProxyInstance(
            target.getClass().getClassLoader(), // 类加载器
            target.getClass().getInterfaces(),  // 目标类实现的接口
            new LogInvocationHandler(target)    // 增强处理器
        );

        // 调用代理方法
        proxy.addUser("王五");
        System.out.println("-----");
        proxy.deleteUser("赵六");
    }
}

运行结果与静态代理一致,但无需手动编写UserServiceProxy—— 代理类在运行时自动生成。

2. CGLIB 动态代理(基于继承)

CGLIB(Code Generation Library)是第三方库,不要求目标类实现接口,而是通过 “继承目标类生成子类” 作为代理类(子类重写父类方法,添加增强逻辑)。

核心原理:
  • 基于 ASM 字节码技术,在运行时生成目标类的子类(代理类);
  • 代理类重写目标类的非 final 方法,在方法中添加增强逻辑;
  • 由于是继承,目标类不能被final修饰(否则无法继承),目标方法也不能是final(否则无法重写)。
代码示例(目标类无需实现接口):
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
import java.util.Date;

// 1. 目标类(无需实现接口)
class OrderService {
    public void createOrder(String orderNo) {
        System.out.println("【核心业务】创建订单:" + orderNo);
    }
}

// 2. 定义增强逻辑(实现MethodInterceptor)
class LogMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        // 增强逻辑:前置处理
        System.out.println("===== 日志开始 =====");
        System.out.println("时间:" + new Date());
        System.out.println("调用方法:" + method.getName());
        System.out.println("参数:" + (args != null ? args[0] : "无"));

        // 调用目标类的方法(通过方法代理)
        Object result = methodProxy.invokeSuper(proxy, args);

        // 增强逻辑:后置处理
        System.out.println("方法执行完成");
        System.out.println("===== 日志结束 =====");

        return result;
    }
}

// 3. 使用CGLIB生成代理
public class CglibProxyDemo {
    public static void main(String[] args) {
        // 创建增强器
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(OrderService.class); // 设置父类(目标类)
        enhancer.setCallback(new LogMethodInterceptor()); // 设置增强逻辑

        // 生成代理对象(目标类的子类)
        OrderService proxy = (OrderService) enhancer.create();

        // 调用代理方法
        proxy.createOrder("ORD_001");
    }
}

运行结果:

===== 日志开始 =====
时间:Wed Sep 10 17:00:00 CST 2025
调用方法:createOrder
参数:ORD_001
【核心业务】创建订单:ORD_001
方法执行完成
===== 日志结束 =====

五、静态代理 vs 动态代理

维度 静态代理 动态代理(JDK/CGLIB)
代理类生成时机 编译期(手动编写) 运行时(自动生成)
代码冗余度 高(每个目标类对应一个代理类) 低(一个增强逻辑可复用)
灵活性 低(接口变更需同步修改代理类) 高(接口变更无需修改代理逻辑)
依赖 JDK 动态代理依赖 JDK 反射;CGLIB 依赖第三方库
适用场景 简单场景(如固定少数类的增强) 复杂场景(如 AOP、RPC 框架)

六、代理模式的典型应用

  1. AOP(面向切面编程):如 Spring AOP,通过动态代理在方法前后插入日志、事务、权限等切面逻辑;
  2. RPC 框架:如 Dubbo,代理对象封装网络通信细节(序列化、网络调用、反序列化),让调用者透明使用远程服务;
  3. 延迟加载:如 Hibernate 的懒加载,通过代理对象延迟加载关联数据(用到时才查询数据库);
  4. 权限控制:代理对象先校验用户权限,再决定是否调用目标方法。

总结

总的来说,代理模式通过 “代理对象” 实现对 “目标对象” 的间接访问,核心价值是控制访问 + 功能增强

  • 静态代理简单但冗余,适合简单场景;
  • 动态代理(JDK/CGLIB)灵活且复用性高,是框架级开发的首选(如 Spring、Dubbo)。

所以对于我们来说,理解代理模式是掌握 AOP、RPC 等核心技术的基础。好了,今天依旧是深蹲不写BUG,我们一起加油努力!!!


网站公告

今日签到

点亮在社区的每一天
去签到