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

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

相关推荐
⑩-3 小时前
SpringCloud-Sleuth链路追踪实战
后端·spring·spring cloud
冷崖3 小时前
原子锁操作
c++·后端
moxiaoran57533 小时前
Spring AOP开发的使用场景
java·后端·spring
一线大码7 小时前
Gradle 基础篇之基础知识的介绍和使用
后端·gradle
Java猿_7 小时前
Spring Boot 集成 Sa-Token 实现登录认证与 RBAC 权限控制(实战)
android·spring boot·后端
小王师傅668 小时前
【轻松入门SpringBoot】actuator健康检查(上)
java·spring boot·后端
码事漫谈8 小时前
C++高并发编程核心技能解析
后端
码事漫谈8 小时前
C++与浏览器交织-从Chrome插件到WebAssembly,开启性能之门
后端
源代码•宸9 小时前
goframe框架签到系统项目(BITFIELD 命令详解、Redis Key 设计、goframe 框架教程、安装MySQL)
开发语言·数据库·经验分享·redis·后端·mysql·golang
⑩-9 小时前
SpringCloud-Nacos 配置中心实战
后端·spring·spring cloud