动态代理笔记(自用)

devtools/2024/9/25 15:20:10/

文章目录

  • 一.动态代理是什么
  • 二.jdk代理
    • 工作原理
    • 使用步骤
      • 步骤 1: 定义接口
      • 步骤 2: 实现接口
      • 步骤 3: 创建调用处理器
      • 步骤 4: 创建代理实例
  • 三.CGLIB代理
      • 工作原理
      • 关键特性
      • 使用示例
  • 四.对比
      • 1. 接口与类
      • 2. 性能
      • 3. 使用复杂度
      • 4. 兼容性和限制
      • 5. 应用场景

一.动态代理是什么

动态代理是一种编程模式。通过动态代理,我们可以在运行时动态地创建一个代理对象,这个代理对象可以代表一个实际的对象,拦截对实际对象的所有方法调用,从而可以在调用实际方法前后添加自定义行为。

总结:代理对象拦截实际调用,在调用前后添加行为

二.jdk代理

工作原理

两个核心组件:Proxy类和InvocationHandler接口。

  1. Proxy类

    • Proxy类是java.lang.reflect包中的一部分,有一个静态方法newProxyInstance,这个方法用于在运行时动态创建代理实例。
    • newProxyInstance方法需要三个参数:类加载器(用于加载代理类)、一组接口(代理类需要实现的接口)、以及一个调用处理器(当代理的方法被调用时,会转发到这个调用处理器上)。
  2. InvocationHandler接口

    • 任何动态代理的实现都必须实现InvocationHandler接口,这个接口中只定义了一个方法invoke,这个方法会在代理实例上的方法被调用时执行。
    • invoke方法有三个参数:代理实例本身(通常不使用)、正在被调用的方法对象以及方法调用的参数数组。方法可以根据需要进行增强,并通过反射调用实际对象的同名方法。

在Java中,使用JDK动态代理通常涉及以下几个主要步骤,这些步骤详细说明了如何创建和使用动态代理来拦截和增强特定的接口方法调用:

使用步骤

步骤 1: 定义接口

定义接口,这个接口是我们接下来需要做增强的接口

java">public interface Greeting {void greet(String name);
}

步骤 2: 实现接口

创见实际类实现这个接口

java">public class EnglishGreeting implements Greeting {public void greet(String name) {System.out.println("Hello, " + name + "!");}
}

步骤 3: 创建调用处理器

创建一个实现了InvocationHandler接口的类。这个类里面的invoke方法里面可以写我们的增强方法

java">import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;public class GreetingInvocationHandler implements InvocationHandler {private Object target;public GreetingInvocationHandler(Object target) {this.target = target;  // 绑定一个实际对象}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("Before method call");Object result = method.invoke(target, args);  // 调用实际对象的方法System.out.println("After method call");return result;}
}

步骤 4: 创建代理实例

使用Proxy.newProxyInstance方法创建动态代理对象。
需要三个参数:一个类加载器,一个需要实现的接口数组,以及你创建的调用处理器:

java">import java.lang.reflect.Proxy;public class Main {public static void main(String[] args) {Greeting realObject = new EnglishGreeting();Greeting proxy = (Greeting) Proxy.newProxyInstance(Greeting.class.getClassLoader(),new Class[] { Greeting.class },new GreetingInvocationHandler(realObject));proxy.greet("John");}
}

三.CGLIB代理

工作原理

CGLIB通过使用底层的ASM(一个Java字节码操纵框架)来操作字节码并生成新的类。它不是代理接口,而是在运行时生成给定类的一个子类,并在子类中覆盖父类的方法。通过这种方式,CGLIB可以在调用方法时插入自定义逻辑,而不改变原有类的代码。

关键特性

  1. 方法拦截:CGLIB使用MethodInterceptor接口来拦截对目标方法的调用。开发者需要实现这个接口,然后在intercept方法中定义拦截逻辑。

  2. 无需接口:与JDK动态代理相比,CGLIB不需要目标对象实现任何接口,这是它的一个显著优势。

  3. 性能考虑:虽然CGLIB在某些情况下比JDK动态代理慢(例如创建代理实例的过程),但在代理方法调用的性能上,CGLIB通常可以提供与JDK动态代理相比更优或相当的性能。

使用示例

java">import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.Method;// 定义一个普通类
class SampleClass {public void test() {System.out.println("Hello CGLIB!");}
}// 实现MethodInterceptor接口
class SampleInterceptor implements MethodInterceptor {@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("Before method");Object result = proxy.invokeSuper(obj, args);System.out.println("After method");return result;}
}// 使用CGLIB创建代理类
public class CglibDemo {public static void main(String[] args) {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(SampleClass.class);enhancer.setCallback(new SampleInterceptor());SampleClass proxy = (SampleClass) enhancer.create();proxy.test();  // Output includes before and after messages}
}

在这个例子中,SampleClass是一个没有实现任何接口的简单类。我们通过CGLIB的Enhancer类创建了SampleClass的代理,并通过SampleInterceptor在原有方法执行前后添加了额外的输出。

四.对比

1. 接口与类

  • JDK动态代理:仅能代理实现了接口的类。如果要使用JDK代理,类必须实现一个或多个接口。
  • CGLIB:能代理没有实现接口的类。它通过在运行时生成目标类的子类来实现代理功能,从而可以代理任何类。

2. 性能

  • JDK动态代理:在生成代理的性能上比CGLIB快,因为它仅仅是反射调用接口的方法。但是在方法调用上,每次都通过反射,可能会稍微慢一些。
  • CGLIB:生成代理的过程比较慢,因为需要通过字节码技术生成类的子类。但是,一旦类被创建,方法调用的性能可能优于或相当于JDK动态代理,因为它是直接调用类的方法,而不是通过反射。

3. 使用复杂度

  • JDK动态代理:使用较为简单,只需要定义接口和实现InvocationHandler接口。Java标准库已内置支持,无需额外引入库。
  • CGLIB:需要引入CGLIB库,使用稍微复杂一些,因为你需要处理更低层次的细节,如方法拦截和增强。配置也稍微复杂,因为需要理解如何操作字节码和类加载器。

4. 兼容性和限制

  • JDK动态代理:不能对类本身进行代理,只能代理接口。如果目标对象没有实现任何接口,则无法使用JDK代理。
  • CGLIB:无法代理声明为final的方法,因为这些方法不能被子类覆盖。此外,对于使用CGLIB增强的类,如果它有复杂的构造函数或者需要特殊方式初始化的类,可能会更加复杂。

5. 应用场景

  • JDK动态代理:适用于那些已经定义了接口的模块。例如,在Java EE或Spring框架中,许多服务已经是基于接口的设计。
  • CGLIB:适用于动态代理那些不便于或不能改造为接口的遗留代码。它也常用于那些由第三方库定义的类,这些类你无法修改其源代码来添加接口。

http://www.ppmy.cn/devtools/18449.html

相关文章

leetcode刷题记录

目录 字符串 无重复字符的最长子串(力扣3) 给定一个字符串 s ,请你找出其中不含有重复字符的 最长 子串 的长度。 示例 1: 输入: s "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 "abc",所以其长…

docker入门学习

一、docker概念 Docker 引擎是使用的是Linux内核特性的容器引擎。 二、docker的安装 1.docker,下载地址: 桌面版:Docker Desktop: The #1 Containerization Tool for Developers | Docker 服务器版:Install Docker Engine | D…

目前软件测试前景怎么样?有哪些机遇和挑战?

随着信息技术的快速发展,软件已经成为了我们生活中不可或缺的一部分。而软件的质量和稳定性也直接关系到用户的使用体验和企业的竞争力。因此,软件测试作为软件质量保证的重要环节,其前景也备受关注。 首先,从行业角度来看&#x…

新建云仓库

1.GitHub新建云仓库: LICENSE:开源许可证;README.md:仓库说明文件;开源项目;cocoaPodsName.podspec: CocoaPods项目的属性描述文件。 2.Coding新建云仓库: 备注: Coding新建项目:

一个不太好用的弹出层jquery.colorbox第二次点击不出来的解决方法

使用jquery.colorbox的时候第一次正常显示,但第二次的时候不显示。 需要先移除,再重新点击即可,代码如下 $.colorbox.remove();$.colorbox({href: url,iframe: true,width: options.width || 800,height: options.height || 600 });

iOS马甲包是什么?

互联网的各种推广营销手法,做到一定程度都会向规模化发展,行内喜欢称之为“APP矩阵”,比如 SEO 领域的站群,新媒体领域的新媒体账号矩阵。App 推广中有一种手法叫马甲包,在我看来也是矩阵思维的一种体现。 马甲包的操…

Java(IO异常解释(为什么要捕获异常,为什么要给NULL)

实现copy的代码: package a0420.iotest1.Test2;import java.io.IOException;public class Test {public static void main(String[] args) {CopyMethod.FileCopy("D:\\idealTestio\\copy.txt","D:\\idealTestio\\finalPase");} }主要想解释一下…

算法打卡day39

今日任务: 1)卡码网57. 爬楼梯(70. 爬楼梯进阶版) 2)322.零钱兑换 3)279.完全平方数 4)复习day14 卡码网57. 爬楼梯(70. 爬楼梯进阶版) 题目链接:57. 爬楼梯…