java动态代理

动态代理

动态代理是一种在运行时创建代理对象的技术,它能够在不修改目标对象源代码的情况下,对目标对象的方法调用进行增强或控制。以下从几个方面详细介绍动态代理:

1. 基本概念
  • 代理对象:动态代理会创建一个代理对象,这个代理对象会代替目标对象接收外部的方法调用。它看起来和目标对象具有相同的接口,外部调用者可以像调用目标对象一样调用代理对象的方法。

  • 目标对象:也就是被代理的实际对象,它包含了真正要执行的业务逻辑。代理对象最终会将接收到的方法调用转发给目标对象去执行。

2. 工作原理
  • 当外部调用者调用代理对象的某一方法时,代理对象首先会拦截这个方法调用。然后,它可以根据具体需求进行一些前置处理,比如进行权限验证、日志记录等操作。

  • 完成前置处理后,代理对象会将方法调用转发给目标对象,让目标对象执行真正的业务逻辑。

  • 在目标对象执行完业务逻辑返回结果后,代理对象还可以进行一些后置处理,比如对返回结果进行加工、统计方法执行时间等操作,最后再将处理后的结果返回给外部调用者。

3. 实现方式(以 Java 为例)

Java 中有两种常见的实现动态代理的方式:基于接口的动态代理(使用 Java 反射机制和java.lang.reflect.Proxy类)和基于类的动态代理(使用第三方库如 CGLIB)。

基于接口的动态代理

  • 要求目标对象必须实现一个或多个接口。

  • 示例代码如下:

复制代码
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
 ​
 // 定义接口
 interface HelloWorld {
     void sayHello();
 }
 ​
 // 实现接口的目标对象
 class HelloWorldImpl implements HelloWorld {
     @Override
     public void sayHello() {
         System.out.println("Hello, World!");
     }
 }
 ​
 // 实现InvocationHandler接口,用于处理代理对象的方法调用
 class MyInvocationHandler implements InvocationHandler {
     private final Object target;
 ​
     public MyInvocationHandler(Object target) {
         this.target = target;
     }
 ​
     @Override
     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
         // 前置处理,比如记录日志
         System.out.println("Before method call: " + method.getName());
 ​
         // 调用目标对象的方法并获取结果
         Object result = method.invoke(target, args);
 ​
         // 后置处理,比如统计方法执行时间
         System.out.println("After method call: " + method.getName());
 ​
         return result;
     }
 }
 ​
 public class DynamicProxyExample {
     public static void main(String[] args) {
         // 创建目标对象
         HelloWorld helloWorld = new HelloWorldImpl();
 ​
         // 创建InvocationHandler对象
         MyInvocationHandler handler = new MyInvocationHandler(helloWorld);
 ​
         // 创建代理对象
         HelloWorld proxy = (HelloWorld) Proxy.createProxyInstance(HelloWorld.class.getClassLoader(),
                 new Class[]{HelloWorld.class}, handler);
 ​
         // 调用代理对象的方法
         proxy.sayHello();
     }
 }

在上述代码中:

  • 首先定义了HelloWorld接口和实现该接口的HelloWorldImpl目标对象。

  • 然后创建了MyInvocationHandler类,它实现了InvocationHandler接口,用于处理代理对象的方法调用。在invoke方法中实现了前置处理、调用目标对象方法以及后置处理的功能。

  • 最后通过Proxy.createProxyInstance方法创建了代理对象,并调用代理对象的方法,此时可以看到前置处理和后置处理的效果。

基于类的动态代理(以 CGLIB 为例)

  • 不需要目标对象实现接口,它可以直接代理类。

  • 示例代码如下:

复制代码
 import net.sf.cglib.proxy.Enhancer;
 import net.sf.cglib.proxy.MethodInterceptor;
 import net.sf.cglib.proxy.MethodProxy;
 ​
 // 目标对象,没有实现任何接口
 class HelloWorld {
     public void sayHello() {
         System.out.println("Hello, World!");
     }
 }
 ​
 // 实现MethodInterceptor接口,用于处理代理对象的方法调用
 class MyMethodInterceptor implements MethodInterceptor {
     @Override
         public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
             // 前置处理,比如记录日志
             System.out.println("Before method call: " + method.getName());
 ​
             // 调用目标对象的方法并获取结果
             Object result = proxy.invokeSuper(obj, args);
 ​
             // 后置处理,比如统计方法执行时间
             System.out.println("After method call: " + method.getName());
 ​
             return result;
         }
 }
 ​
 public class CglibDynamicProxyExample {
     public static void main(String[] args) {
         // 创建目标对象
         HelloWorld helloWorld = new HelloWorld();
 ​
         // 创建Enhancer对象
         Enhancer enhancer = new Enhancer();
 ​
         // 设置目标对象的类
         enhancer.setTarget(helloWorld);
 ​
         // 设置MethodInterceptor
         enhancer.setCallback(new MyMethodInterceptor());
 ​
         // 创建代理对象
         HelloWorld proxy = (HelloWorld) enhancer.create();
 ​
         // 调用代理对象的方法
         proxy.sayHello();
     }
 }

在上述代码中:

  • 定义了没有实现任何接口的HelloWorld目标对象。

  • 然后创建了MyMethodInterceptor类,它实现了MethodInterceptor接口,用于处理代理对象的方法调用。在intercept方法中实现了前置处理、调用目标对象方法以及后置处理的功能。

  • 最后通过Enhancer创建了代理对象,并调用代理对象的方法,同样可以看到前置处理和后置处理的效果。

4. 应用场景
  • 权限控制:在企业级应用中,对于不同用户访问某些敏感业务方法时,可以通过动态代理在方法调用前进行权限验证,只有具备相应权限的用户才能访问到真正的业务方法。

  • 日志记录:为了方便跟踪和分析应用程序的运行情况,可以在代理对象的方法调用前后分别进行日志记录,记录下方法的名称、参数、执行时间等信息,以便于后续的故障排查和性能分析。

  • 性能监控:通过动态代理可以统计目标对象的方法执行时间,以此来评估业务方法的执行效率,及时发现可能存在的性能问题并进行优化。

动态代理是一种非常有用的技术,它为软件开发提供了一种灵活的方式来对目标对象的方法调用进行增强和控制,在许多应用场景中都发挥着重要作用。

相关推荐
二十七剑2 分钟前
jvm中各个参数的理解
java·jvm
东阳马生架构1 小时前
JUC并发—9.并发安全集合四
java·juc并发·并发安全的集合
计算机小白一个2 小时前
蓝桥杯 Java B 组之岛屿数量、二叉树路径和(区分DFS与回溯)
java·数据结构·算法·蓝桥杯
White graces2 小时前
正则表达式效验邮箱格式, 手机号格式, 密码长度
前端·spring boot·spring·正则表达式·java-ee·maven·intellij-idea
菠菠萝宝2 小时前
【Java八股文】10-数据结构与算法面试篇
java·开发语言·面试·红黑树·跳表·排序·lru
不会Hello World的小苗2 小时前
Java——链表(LinkedList)
java·开发语言·链表
Allen Bright3 小时前
【Java基础-46.3】Java泛型通配符详解:解锁类型安全的灵活编程
java·开发语言
柃歌3 小时前
【UCB CS 61B SP24】Lecture 7 - Lists 4: Arrays and Lists学习笔记
java·数据结构·笔记·学习·算法
柃歌3 小时前
【UCB CS 61B SP24】Lecture 4 - Lists 2: SLLists学习笔记
java·数据结构·笔记·学习·算法
是姜姜啊!3 小时前
redis的应用,缓存,分布式锁
java·redis·spring