静态代理与动态代理:深入理解Java代理模式

代理模式概述

在软件开发中,代理模式是一种常见的设计模式,它为其他对象提供一种代理以控制对这个对象的访问。代理对象在客户端和目标对象之间起到中介作用,可以在不修改目标对象代码的前提下,增加额外的功能或控制访问。

静态代理:编译时确定的代理

什么是静态代理?

静态代理是指在编译时期就已经确定代理类与被代理类的关系。代理类和目标类实现相同的接口,代理类内部持有目标对象的引用,并在调用目标方法前后添加额外的逻辑。

静态代理的实现

让我们通过一个简单的例子来理解静态代理:

java 复制代码
// 1. 定义接口
interface UserService {
    void addUser(String username);
    void deleteUser(String username);
}

// 2. 实现目标类
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. 创建代理类
class UserServiceProxy implements UserService {
    private UserService target; // 目标对象
    
    public UserServiceProxy(UserService target) {
        this.target = target;
    }
    
    @Override
    public void addUser(String username) {
        System.out.println("【代理】开始添加用户...");
        long start = System.currentTimeMillis();
        
        // 调用目标方法
        target.addUser(username);
        
        long end = System.currentTimeMillis();
        System.out.println("【代理】添加用户完成,耗时: " + (end - start) + "ms");
    }
    
    @Override
    public void deleteUser(String username) {
        System.out.println("【代理】开始删除用户...");
        long start = System.currentTimeMillis();
        
        // 调用目标方法
        target.deleteUser(username);
        
        long end = System.currentTimeMillis();
        System.out.println("【代理】删除用户完成,耗时: " + (end - start) + "ms");
    }
}

// 4. 客户端使用
public class StaticProxyDemo {
    public static void main(String[] args) {
        // 创建目标对象
        UserService target = new UserServiceImpl();
        
        // 创建代理对象,并传入目标对象
        UserService proxy = new UserServiceProxy(target);
        
        // 通过代理对象调用方法
        proxy.addUser("张三");
        proxy.deleteUser("李四");
    }
}

静态代理的优缺点

优点:

  • 简单直观,易于理解和实现
  • 在不修改目标对象的前提下扩展功能
  • 符合开闭原则

缺点:

  • 代理类和目标类必须实现相同的接口,如果接口有多个方法,代理类需要重写所有方法
  • 每个代理类只能代理一个接口,如果有多个接口需要代理,需要创建多个代理类
  • 代理类在编译时就已经确定,不够灵活

动态代理:运行时生成的代理

什么是动态代理?

动态代理是指在程序运行时动态生成代理类。Java提供了两种主要的动态代理机制:

  1. JDK动态代理:基于接口的代理
  2. CGLIB动态代理:基于类继承的代理

JDK动态代理

JDK动态代理利用Java的反射机制,在运行时创建代理类。它要求目标类必须实现接口。

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

// 1. 定义接口和目标类(与静态代理相同)
interface OrderService {
    void createOrder(String orderId);
    void cancelOrder(String orderId);
}

class OrderServiceImpl implements OrderService {
    @Override
    public void createOrder(String orderId) {
        System.out.println("创建订单: " + orderId);
    }
    
    @Override
    public void cancelOrder(String orderId) {
        System.out.println("取消订单: " + orderId);
    }
}

// 2. 创建InvocationHandler实现类
class LoggingHandler implements InvocationHandler {
    private Object target; // 目标对象
    
    public LoggingHandler(Object target) {
        this.target = target;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String methodName = method.getName();
        System.out.println("【动态代理】开始执行方法: " + methodName);
        long start = System.currentTimeMillis();
        
        // 调用目标方法
        Object result = method.invoke(target, args);
        
        long end = System.currentTimeMillis();
        System.out.println("【动态代理】方法执行完成,耗时: " + (end - start) + "ms");
        
        return result;
    }
}

// 3. 动态代理工厂类
class ProxyFactory {
    public static Object createProxy(Object target) {
        return Proxy.newProxyInstance(
            target.getClass().getClassLoader(), // 类加载器
            target.getClass().getInterfaces(),  // 目标对象实现的接口
            new LoggingHandler(target)          // InvocationHandler
        );
    }
}

// 4. 客户端使用
public class DynamicProxyDemo {
    public static void main(String[] args) {
        // 创建目标对象
        OrderService target = new OrderServiceImpl();
        
        // 创建动态代理
        OrderService proxy = (OrderService) ProxyFactory.createProxy(target);
        
        // 通过代理对象调用方法
        proxy.createOrder("ORD001");
        proxy.cancelOrder("ORD002");
        
        // 可以查看代理类的信息
        System.out.println("代理类: " + proxy.getClass().getName());
        System.out.println("代理类的父类: " + proxy.getClass().getSuperclass().getName());
        System.out.println("代理类实现的接口: ");
        for (Class<?> clazz : proxy.getClass().getInterfaces()) {
            System.out.println("  - " + clazz.getName());
        }
    }
}

CGLIB动态代理

CGLIB(Code Generation Library)是一个强大的、高性能的代码生成库,它可以在运行时扩展Java类和实现Java接口。与JDK动态代理不同,CGLIB可以代理没有实现接口的类。

java 复制代码
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

// 1. 目标类(不需要实现接口)
class ProductService {
    public void addProduct(String productName) {
        System.out.println("添加产品: " + productName);
    }
    
    public void updateProduct(String productName) {
        System.out.println("更新产品: " + productName);
    }
}

// 2. 创建MethodInterceptor实现类
class CglibProxyInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        String methodName = method.getName();
        System.out.println("【CGLIB代理】开始执行方法: " + methodName);
        long start = System.currentTimeMillis();
        
        // 调用目标方法
        Object result = proxy.invokeSuper(obj, args);
        
        long end = System.currentTimeMillis();
        System.out.println("【CGLIB代理】方法执行完成,耗时: " + (end - start) + "ms");
        
        return result;
    }
}

// 3. CGLIB代理工厂
class CglibProxyFactory {
    public static Object createProxy(Class<?> targetClass) {
        // 创建增强器
        Enhancer enhancer = new Enhancer();
        
        // 设置父类(目标类)
        enhancer.setSuperclass(targetClass);
        
        // 设置回调
        enhancer.setCallback(new CglibProxyInterceptor());
        
        // 创建代理对象
        return enhancer.create();
    }
}

// 4. 客户端使用
public class CglibProxyDemo {
    public static void main(String[] args) {
        // 创建CGLIB代理
        ProductService proxy = (ProductService) CglibProxyFactory.createProxy(ProductService.class);
        
        // 通过代理对象调用方法
        proxy.addProduct("笔记本电脑");
        proxy.updateProduct("智能手机");
        
        // 可以查看代理类的信息
        System.out.println("代理类: " + proxy.getClass().getName());
        System.out.println("代理类的父类: " + proxy.getClass().getSuperclass().getName());
    }
}

动态代理的优缺点

优点:

  • 高度灵活,一个代理类可以代理多个接口
  • 减少代码量,不需要为每个目标类创建代理类
  • 可以在运行时决定代理哪个对象

缺点:

  • JDK动态代理只能代理实现了接口的类
  • CGLIB不能代理final类或final方法
  • 动态代理的性能略低于静态代理(但影响通常很小)

静态代理 vs 动态代理:核心对比

特性 静态代理 动态代理
实现时机 编译时 运行时
实现方式 手动编写代理类 通过反射或字节码生成
灵活性 低,一个代理类只能代理一个接口 高,一个代理类可以代理多个接口
代码量 多,需要为每个目标类编写代理类 少,通过通用代码处理多个类
性能 略高(直接调用) 略低(反射调用)
适用场景 接口方法少,代理逻辑简单 接口方法多,代理逻辑复杂

实际应用场景

静态代理适用场景

  • AOP(面向切面编程)的简单实现
  • 权限控制
  • 本地远程代理

动态代理适用场景

  1. Spring AOP:Spring框架使用JDK动态代理和CGLIB实现AOP
  2. MyBatis:Mapper接口的代理实现
  3. RPC框架:远程方法调用的客户端代理
  4. 事务管理:声明式事务的实现
  5. 日志记录:方法调用的日志记录

Spring框架中的代理示例

java 复制代码
// Spring中常用的注解驱动代理
@Service
@Transactional
public class UserServiceImpl implements UserService {
    
    @Override
    @LogExecutionTime // 自定义注解,通过AOP实现方法执行时间日志
    public User findUserById(Long id) {
        // 业务逻辑
        return userRepository.findById(id);
    }
    
    @Override
    @PreAuthorize("hasRole('ADMIN')") // Spring Security权限控制
    public void deleteUser(Long id) {
        // 业务逻辑
        userRepository.deleteById(id);
    }
}

总结

代理模式是Java中非常重要且实用的设计模式。静态代理简单直观,适合简单的代理需求;动态代理灵活强大,适合复杂的系统架构。理解两者的原理和适用场景,能够帮助我们在实际开发中做出更合适的技术选择。

无论是静态代理还是动态代理,它们都体现了面向对象设计的一个重要原则:对扩展开放,对修改关闭。通过代理模式,我们可以在不修改原有代码的基础上,为系统添加新的功能,这大大提高了代码的可维护性和可扩展性。

相关推荐
pilaf199036 分钟前
Rust练习题
开发语言·后端·rust
vx_bisheyuange41 分钟前
基于SpringBoot的交通在线管理服务系统
java·spring boot·后端·毕业设计
小坏讲微服务1 小时前
Spring Boot 4.0 与 MyBatis Plus 整合完整指南
java·spring boot·后端·mybatis·springcloud·mybatis plus·java开发
番茄Salad1 小时前
Spring Boot项目,修改项目名称,修改包名!
java·spring boot·后端
疯狂的程序猴1 小时前
Fiddler调试工具全面解析 HTTPHTTPS抓包、代理设置与接口测试实战教程
后端
极市平台1 小时前
骁龙大赛技术分享第4期来了
人工智能·经验分享·笔记·后端·个人开发
开心就好20251 小时前
Charles抓包工具使用方法 Charles抓包分析、配置教程、网络排查技巧与手机抓包步骤
后端
sheji34161 小时前
【开题答辩全过程】以 基于springboot游泳馆管理系统为例,包含答辩的问题和答案
java·spring boot·后端
5***r9351 小时前
SpringBoot 与 SpringCloud的版本对应详细版
spring boot·后端·spring cloud