当前位置: 首页 > news >正文

动态代理示例解析

动态代理

文章目录

  • 动态代理
    • 先来看一个示例
      • 定义接口
      • 实现接口
      • 创建动态代理类
      • 测试动态代理
      • 输出结果
      • 关键点解析
    • CGLIB 动态代理
      • 定义目标类(无需实现接口)
      • 创建 CGLIB 动态代理
      • 测试代码
    • JDK 动态代理 vs CGLIB 动态代理
    • 为什么需要使用代理对象?
    • 动态代理适合的场景
    • 动态代理的优势
    • 什么时候不适合使用动态代理?
    • Spring 中动态代理的主要应用场景包括:
      • AOP(面向切面编程)
      • 事务管理
    • Spring 中动态代理的主要应用场景包括:
      • AOP(面向切面编程)
      • 事务管理

先来看一个示例

定义接口

// 定义一个业务接口
public interface UserService {
    void addUser(String name);
    void deleteUser(String name);
}

实现接口

// 实现接口的业务逻辑
public class UserServiceImpl implements UserService {
    @Override
    public void addUser(String name) {
        System.out.println("添加用户: " + name);
    }

    @Override
    public void deleteUser(String name) {
        System.out.println("删除用户: " + name);
    }
}

创建动态代理类

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 动态代理处理器
public class UserServiceProxyHandler implements InvocationHandler {
    private Object target; // 被代理的目标对象

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 方法调用前增强逻辑
        System.out.println("代理前: 准备执行方法 " + method.getName());

        // 执行目标方法
        Object result = method.invoke(target, args);

        // 方法调用后增强逻辑
        System.out.println("代理后: 方法 " + method.getName() + " 执行完成");

        return result;
    }

    // 获取代理对象
    public Object getProxy() {
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),         // 类加载器
                target.getClass().getInterfaces(),          // 目标对象实现的接口
                this                                       // 当前InvocationHandler实例
        );
    }
}

测试动态代理

public class DynamicProxyTest {
    public static void main(String[] args) {
        // 创建目标对象
        UserService userService = new UserServiceImpl();

        // 创建代理处理器并获取代理对象
        UserService proxyService = (UserService) new UserServiceProxyHandler(userService).getProxy();

        // 调用代理对象的方法
        proxyService.addUser("Alice");
        proxyService.deleteUser("Bob");
    }
}

输出结果

代理前: 准备执行方法 addUser
添加用户: Alice
代理后: 方法 addUser 执行完成
代理前: 准备执行方法 deleteUser
删除用户: Bob
代理后: 方法 deleteUser 执行完成

关键点解析

1.InvocationHandler 接口

  • proxy: 代理对象本身(通常不直接使用)
  • method: 被调用的目标方法
  • args: 方法参数

2.Proxy.newProxyInstance 方法

  • ClassLoader: 目标对象的类加载器
  • Class<?>[]: 目标对象实现的接口列表
  • InvocationHandler: 处理代理逻辑的处理器

3.适用场景

  • JDK 动态代理要求目标对象必须实现接口。如果目标对象没有实现接口,可以使用 CGLIB 动态代理。

4.扩展性

  • 可以在 invoke 方法中加入更多功能,如日志记录、性能监控、事务管理等。

CGLIB 动态代理

CGLIB 动态代理不要求目标对象实现接口,代理通过字节码生成子类的方式实现。

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>

定义目标类(无需实现接口)

public class UserService {
    public void addUser(String name) {
        System.out.println("添加用户: " + name);
    }

    public void deleteUser(String name) {
        System.out.println("删除用户: " + name);
    }
}

创建 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 CglibProxyHandler implements MethodInterceptor {
    private Object target; // 被代理的目标对象

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

    public Object getProxy() {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target.getClass()); // 设置父类为目标类
        enhancer.setCallback(this);               // 设置回调
        return enhancer.create();                 // 创建代理对象
    }

    @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;
    }
}

测试代码

public class CglibProxyTest {
    public static void main(String[] args) {
        UserService userService = new UserService();
        UserService proxyService = (UserService) new CglibProxyHandler(userService).getProxy();

        proxyService.addUser("Alice");
        proxyService.deleteUser("Bob");
    }
}

JDK 动态代理 vs CGLIB 动态代理

特性JDKL动态代理CGLB动态代理
实现方式基于接口基于继承(生成子类)
目标对象要求必须实现接口不需要实现接口
性能接口调用效率较高子类生成效率较低,但运行时性能接近
适用场景目标对象有接口目标对象无接口或需要代理类本身的方法
局限性无法代理类本身的非接口方法无法代理 final 类或 final 方法

为什么需要使用代理对象?

即使你已经通过 new 创建了具体的对象,直接使用它也能完成业务逻辑,但这种方式存在以下局限性:

1. 代码侵入性强

如果你希望在方法调用前后添加一些通用逻辑(如日志记录、性能监控、事务管理等),直接修改原始类的代码会破坏单一职责原则,并且会导致代码冗余。

2.功能扩展困难

假设你需要为某些方法添加权限校验或缓存支持,直接修改原始类会让代码变得复杂且容易出错。而代理可以在不修改原始类的情况下,动态地添加这些功能。

3. 解耦与复用

通过代理模式,可以将核心业务逻辑与非核心逻辑(如日志、事务)分离,提升代码的可读性和复用性。

动态代理适合的场景

动态代理的主要目的是在运行时动态地为目标对象添加额外的行为,而不改变其原有代码。以下是动态代理的典型应用场景:

1. 日志记录

在方法调用前后记录日志,便于调试和审计。

System.out.println("代理前: 准备执行方法 " + method.getName());
Object result = method.invoke(target, args);
System.out.println("代理后: 方法 " + method.getName() + " 执行完成");

2.性能监控

统计方法的执行时间,用于性能优化。

long startTime = System.currentTimeMillis();
Object result = method.invoke(target, args);
long endTime = System.currentTimeMillis();
System.out.println("方法执行耗时: " + (endTime - startTime) + "ms");

3. 事务管理

在方法调用前后开启和提交事务,确保数据一致性。

transactionManager.beginTransaction();
try {
    Object result = method.invoke(target, args);
    transactionManager.commit();
    return result;
} catch (Exception e) {
    transactionManager.rollback();
    throw e;
}

4. 权限校验

在方法调用前检查用户是否有权限执行该操作。

if (!securityManager.hasPermission(user, method)) {
    throw new SecurityException("无权访问");
}
return method.invoke(target, args);

5. 缓存支持

在方法调用前检查缓存中是否存在结果,避免重复计算。

String cacheKey = generateCacheKey(method, args);
if (cache.contains(cacheKey)) {
    return cache.get(cacheKey);
}
Object result = method.invoke(target, args);
cache.put(cacheKey, result);
return result;

6. 远程调用(RPC)

在分布式系统中,动态代理可以隐藏远程调用的细节,让开发者像调用本地方法一样调用远程服务。

// 动态代理实现 RPC 调用
Object result = rpcClient.invokeRemoteService(target, method, args);

动态代理的优势

  1. 透明性:动态代理对调用者是透明的,调用者无需关心代理的存在,只需像操作普通对象一样调用方法。
  2. 灵活性:代理逻辑可以在运行时动态生成,适用于多种场景(如日志、事务、权限等),而无需修改原始类。
  3. 解耦:将核心业务逻辑与非核心逻辑分离,符合单一职责原则和开闭原则。

什么时候不适合使用动态代理?

尽管动态代理非常强大,但它也有一些局限性,以下场景可能不适合使用动态代理:

1. 性能敏感场景

动态代理会引入一定的性能开销(尤其是 CGLIB),对于高频调用的场景可能需要权衡。

2. 简单任务

如果只是简单的功能调用,没有额外的需求(如日志、事务等),直接使用原始对象即可。

3. 静态需求

如果增强逻辑是固定的,且不需要动态生成,可以通过继承或装饰器模式实现。

Spring 中动态代理的主要应用场景包括:

AOP(面向切面编程)

  • 用于增强方法调用(如日志记录、性能监控、权限校验等)。
  • 示例:通过 @Aspect 注解定义切面逻辑。

事务管理

  • 用于声明式事务控制。
  • 示例:通过 @Transactional 注解实现事务管理。

定的性能开销(尤其是 CGLIB),对于高频调用的场景可能需要权衡。

2. 简单任务

如果只是简单的功能调用,没有额外的需求(如日志、事务等),直接使用原始对象即可。

3. 静态需求

如果增强逻辑是固定的,且不需要动态生成,可以通过继承或装饰器模式实现。

Spring 中动态代理的主要应用场景包括:

AOP(面向切面编程)

  • 用于增强方法调用(如日志记录、性能监控、权限校验等)。
  • 示例:通过 @Aspect 注解定义切面逻辑。

事务管理

  • 用于声明式事务控制。
  • 示例:通过 @Transactional 注解实现事务管理。

相关文章:

  • Docker DockerFile和Django最佳实践
  • 0321美团实习面试——技能大致内容
  • JVM的一些知识
  • 如何在项目中有效管理设计模式的复杂性
  • 达梦数据库主备切换技术解析与实践指南
  • 《数字图像处理》第三章3.3直方图处理学习笔记
  • Java面试第十一山!《SpringCloud框架》
  • ArcGIS10. 8简介与安装,附下载地址
  • nginx 日志切割
  • Docker进阶篇1:什么是Docker数据卷?为什么需要Docker数据卷?Docker数据卷3种类型介绍
  • 5、MySQL为什么使用 B+树 来作索引【高频】
  • 【机器学习chp14 — 2】生成式模型—变分自编码器VAE(超详细分析,易于理解,推导严谨,一文就够了)
  • 从零开始实现 C++ TinyWebServer 缓冲区 Buffer类详解
  • 【万字总结】前端全方位性能优化指南(一)——Brotli压缩、CDN智能分发、资源合并
  • 界面控件DevExpress WinForms v25.1预览 - 提升.NET开发效率
  • Spark 中agg的用法
  • 《Python深度学习》第四讲:计算机视觉中的深度学习
  • AI实干家:HK深度体验-【第3篇-香港、新加坡、深圳、上海、首尔五座城市在金融数据维度的对比分析】
  • 【机器学习chp14 — 1】生成式模型概述和主要思想(超详细分析,易于理解,推导严谨,一文就够了)
  • ArkTS 基础语法介绍
  • 广汽集团一季度净亏损7.3亿元,同比转亏,总销量下滑9%
  • 刘非履新浙江省委常委、杭州市委书记,曾在吉湘云多省任职
  • 政治局会议:根据形势变化及时推出增量储备政策,加强超常规逆周期调节
  • 2025年度人大立法工作计划将公布:研究启动法律清理工作
  • 民政部党组成员、中国老龄协会会长刘振国任民政部副部长
  • 詹妮弗·劳伦斯、罗伯特·帕丁森新片入围戛纳主竞赛单元