From 7a5b7f77ee35dcefe0951ff9732088a10d706e75 Mon Sep 17 00:00:00 2001 From: linlei Date: Wed, 10 Apr 2024 17:02:06 +0800 Subject: [PATCH] =?UTF-8?q?Cglib=E5=8A=A8=E6=80=81=E4=BB=A3=E7=90=86?= =?UTF-8?q?=E6=BA=90=E7=A0=81=E5=88=86=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + spring-aop/spring-aop-cglibProxy/README.md | 231 ++++++++++++++++++ .../java/com/xcs/spring/CglibProxyDemo.java | 21 ++ .../com/xcs/spring/MyMethodInterceptor.java | 16 ++ .../main/java/com/xcs/spring/MyService.java | 5 + .../java/com/xcs/spring/MyServiceImpl.java | 9 + 6 files changed, 283 insertions(+) create mode 100644 spring-aop/spring-aop-cglibProxy/README.md create mode 100644 spring-aop/spring-aop-cglibProxy/src/main/java/com/xcs/spring/CglibProxyDemo.java create mode 100644 spring-aop/spring-aop-cglibProxy/src/main/java/com/xcs/spring/MyMethodInterceptor.java create mode 100644 spring-aop/spring-aop-cglibProxy/src/main/java/com/xcs/spring/MyService.java create mode 100644 spring-aop/spring-aop-cglibProxy/src/main/java/com/xcs/spring/MyServiceImpl.java diff --git a/README.md b/README.md index d9d792a..d2a2783 100644 --- a/README.md +++ b/README.md @@ -197,6 +197,7 @@ + Spring AOP - [JDK动态代理](spring-aop/spring-aop-jdkProxy/README.md):接口实现,动态生成代理类,处理方法调用,统一横切关注点。 + - [Cglib动态代理](spring-aop/spring-aop-cglibProxy/README.md):基于字节码生成的库,无需接口,可拦截类方法并进行增强。 - [AopProxy](spring-aop/spring-aop-aopProxy/README.md):创建和管理AOP代理对象。 - [ClassFilter](spring-aop/spring-aop-classFilter/README.md):确定类是否匹配拦截条件。 - [MethodMatcher](spring-aop/spring-aop-methodMatcher/README.md):确定方法是否匹配拦截条件。 diff --git a/spring-aop/spring-aop-cglibProxy/README.md b/spring-aop/spring-aop-cglibProxy/README.md new file mode 100644 index 0000000..3b02a95 --- /dev/null +++ b/spring-aop/spring-aop-cglibProxy/README.md @@ -0,0 +1,231 @@ +## Cglib动态代理 + +- [Cglib动态代理](#cglib动态代理) + - [一、基本信息](#一基本信息) + - [二、知识储备](#二知识储备) + - [三、基本描述](#三基本描述) + - [四、主要功能](#四主要功能) + - [五、最佳实践](#五最佳实践) + - [六、源码分析](#六源码分析) + - [七、与其他组件的关系](#七与其他组件的关系) + - [八、常见问题](#八常见问题) + + +### 一、基本信息 + +✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) + +### 二、知识储备 + +1. **理解代理模式** + + + 理解代理模式的基本概念是必要的。了解代理模式如何通过代理对象控制对原始对象的访问,以及代理对象能够附加额外的行为等,有助于理解 Cglib 动态代理的作用和原理。 + +2. **了解字节码生成** + + + Cglib 动态代理是通过生成字节码来创建目标类的子类,并在子类中添加代理逻辑来实现的。因此,了解字节码生成的基本原理以及如何操作字节码是有帮助的。不过,并不要求对字节码生成技术有深入的了解,只需要了解其基本概念即可。 + +3. **熟悉 (AOP) 概念** + + + Cglib 动态代理通常用于实现面向切面编程 (AOP)。了解 AOP 的基本概念、切面、连接点、通知等内容可以帮助理解 Cglib 动态代理在实际项目中的应用场景和使用方式。 + +4. **掌握 Cglib 库的使用方法** + + + 最后,当然需要掌握 Cglib 库的使用方法。包括如何导入 Cglib 库到项目中,以及如何使用 Cglib 提供的 API 来创建代理类、实现方法拦截和增强等操作。 + +### 三、基本描述 + +Cglib 是一个基于 Java 的开源代码生成库,它通过动态生成字节码的方式实现了对类的动态代理,无需目标类实现接口即可进行代理,常用于 AOP 编程、方法拦截与增强等场景,提供了灵活而高效的代理解决方案。 + +### 四、主要功能 + +1. **动态代理生成** + + + Cglib 能够在运行时动态生成类的子类,从而实现对目标类的动态代理,无需目标类实现接口。 + +2. **AOP 支持** + + + Cglib 是 AOP 编程中常用的工具之一,它可以通过代理技术实现方法拦截和增强,方便实现横切关注点的功能。 + +3. **字节码操作** + + + Cglib 允许我们直接操作字节码,从而能够在运行时修改类的结构和行为。 + +4. **高性能** + + + 相对于 JDK 标准库中的动态代理,Cglib 生成的代理类性能更高,因为它直接操作字节码,而不是通过反射调用。 + +5. **无接口代理** + + + 与 JDK 动态代理不同,Cglib 可以代理那些没有实现接口的类,提供了更广泛的应用场景。 + +### 五、最佳实践 + +使用 Cglib 实现动态代理。它首先创建了一个 Enhancer 对象,然后设置了目标对象的父类和回调拦截器,最后通过 Enhancer 创建了代理对象。这个代理对象可以调用目标对象的方法,并且在方法执行前后执行拦截器中定义的逻辑。 + +```java +public class CglibProxyDemo { + + public static void main(String[] args) { + // 创建 Enhancer 对象,用于生成代理类 + Enhancer enhancer = new Enhancer(); + // 设置目标对象的父类 + enhancer.setSuperclass(MyServiceImpl.class); + // 设置回调拦截器 + enhancer.setCallback(new MyMethodInterceptor()); + // 创建代理对象 + MyService proxyObject = (MyService) enhancer.create(); + // 输出代理对象的类名 + System.out.println("ProxyObject = " + proxyObject.getClass()); + // 调用代理对象的方法 + proxyObject.doSomething(); + } +} +``` + +实现了 `MethodInterceptor` 接口的类,用于定义拦截器的行为。在 `intercept` 方法中,它接收被代理对象、目标方法、方法参数以及方法代理对象作为参数,并在目标方法执行前后执行一些逻辑。 + +```java +public class MyMethodInterceptor implements MethodInterceptor { + @Override + public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { + System.out.println("Before invoking method: " + method.getName()); + Object result = methodProxy.invokeSuper(obj, args); + System.out.println("After invoking method: " + method.getName()); + return result; + } +} +``` + +定义了一个接口 `MyService`,其中包含一个抽象方法 `doSomething()`。然后,定义了一个实现了 `MyService` 接口的类 `MyServiceImpl`,并实现了 `doSomething()` 方法。 + +```java +public interface MyService { + void doSomething(); +} + +public class MyServiceImpl implements MyService { + + @Override + public void doSomething() { + System.out.println("hello world"); + } +} +``` + +运行结果,成功创建了代理对象,并且在调用 `doSomething()` 方法前后执行了拦截器中定义的逻辑。 + +```java +ProxyObject = class com.xcs.spring.MyServiceImpl$$EnhancerByCGLIB$$bff4cd04 +Before invoking method: doSomething +hello world +After invoking method: doSomething +``` + +### 六、源码分析 + +这段代码是通过反编译工具(arthas)得到的 Cglib 生成的代理类的源代码。这个代理类继承了目标类 `MyServiceImpl`,并实现了 `Factory` 接口。它重写了目标类的方法,并添加了拦截器逻辑。在每个方法的实现中,先尝试获取拦截器对象,然后通过拦截器的 `intercept` 方法执行拦截逻辑,最终调用目标方法。除此之外,它还包含了一些静态方法和静态字段,用于初始化和支持代理类的其他操作。 + +```java +package com.xcs.spring; + +import com.xcs.spring.MyServiceImpl; +import java.lang.reflect.Method; +import org.springframework.cglib.core.ReflectUtils; +import org.springframework.cglib.core.Signature; +import org.springframework.cglib.proxy.Callback; +import org.springframework.cglib.proxy.Factory; +import org.springframework.cglib.proxy.MethodInterceptor; +import org.springframework.cglib.proxy.MethodProxy; + +public class MyServiceImpl$$EnhancerByCGLIB$$bff4cd04 extends MyServiceImpl implements Factory { + private boolean CGLIB$BOUND; + public static Object CGLIB$FACTORY_DATA; + private static final ThreadLocal CGLIB$THREAD_CALLBACKS; + private static final Callback[] CGLIB$STATIC_CALLBACKS; + private MethodInterceptor CGLIB$CALLBACK_0; + private static Object CGLIB$CALLBACK_FILTER; + private static final Method CGLIB$doSomething$0$Method; + private static final MethodProxy CGLIB$doSomething$0$Proxy; + private static final Object[] CGLIB$emptyArgs; + private static final Method CGLIB$equals$1$Method; + private static final MethodProxy CGLIB$equals$1$Proxy; + private static final Method CGLIB$toString$2$Method; + private static final MethodProxy CGLIB$toString$2$Proxy; + private static final Method CGLIB$hashCode$3$Method; + private static final MethodProxy CGLIB$hashCode$3$Proxy; + private static final Method CGLIB$clone$4$Method; + private static final MethodProxy CGLIB$clone$4$Proxy; + + static void CGLIB$STATICHOOK1() { + CGLIB$THREAD_CALLBACKS = new ThreadLocal(); + CGLIB$emptyArgs = new Object[0]; + Class clazz = Class.forName("com.xcs.spring.MyServiceImpl$$EnhancerByCGLIB$$bff4cd04"); + Class clazz2 = Class.forName("java.lang.Object"); + Method[] methodArray = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, clazz2.getDeclaredMethods()); + CGLIB$equals$1$Method = methodArray[0]; + CGLIB$equals$1$Proxy = MethodProxy.create(clazz2, clazz, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1"); + CGLIB$toString$2$Method = methodArray[1]; + CGLIB$toString$2$Proxy = MethodProxy.create(clazz2, clazz, "()Ljava/lang/String;", "toString", "CGLIB$toString$2"); + CGLIB$hashCode$3$Method = methodArray[2]; + CGLIB$hashCode$3$Proxy = MethodProxy.create(clazz2, clazz, "()I", "hashCode", "CGLIB$hashCode$3"); + CGLIB$clone$4$Method = methodArray[3]; + CGLIB$clone$4$Proxy = MethodProxy.create(clazz2, clazz, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4"); + clazz2 = Class.forName("com.xcs.spring.MyServiceImpl"); + CGLIB$doSomething$0$Method = ReflectUtils.findMethods(new String[]{"doSomething", "()V"}, clazz2.getDeclaredMethods())[0]; + CGLIB$doSomething$0$Proxy = MethodProxy.create(clazz2, clazz, "()V", "doSomething", "CGLIB$doSomething$0"); + } + + final void CGLIB$doSomething$0() { + super.doSomething(); + } + + public final void doSomething() { + MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0; + if (methodInterceptor == null) { + MyServiceImpl$$EnhancerByCGLIB$$bff4cd04.CGLIB$BIND_CALLBACKS(this); + methodInterceptor = this.CGLIB$CALLBACK_0; + } + if (methodInterceptor != null) { + Object object = methodInterceptor.intercept(this, CGLIB$doSomething$0$Method, CGLIB$emptyArgs, CGLIB$doSomething$0$Proxy); + return; + } + super.doSomething(); + } + + // ... [代码部分省略以简化] + + // ... [toString代码部分省略以简化] + // ... [hashCode代码部分省略以简化] + // ... [equals代码部分省略以简化] + // ... [clone代码部分省略以简化] + + // ... [代码部分省略以简化] + static { + MyServiceImpl$$EnhancerByCGLIB$$bff4cd04.CGLIB$STATICHOOK1(); + } +} +``` + +### 七、与其他组件的关系 + +1. **Spring Framework** + + + Spring Framework 是一个全功能的 Java 开发框架,它将 Cglib 集成到其核心模块中,主要用于实现 Spring AOP(面向切面编程)功能。Spring 使用 Cglib 动态生成子类来实现代理,从而在方法执行前后织入额外的逻辑,如事务管理、安全检查等。 + +2. **AspectJ** ++ AspectJ 是一个功能强大的面向切面编程(AOP)框架,它提供了更强大的切面功能。Cglib 可以作为 AspectJ 的后端,用于生成代理类,并通过代理类实现 AOP 功能。在 AspectJ 中,Cglib 代理可以被用于代理任意 Java 类的任意方法。 + +### 八、常见问题 + +1. **Final 类和方法无法代理** + + + 由于 Cglib 是通过生成目标类的子类来实现代理,所以无法代理被 final 修饰的类和方法。如果目标类或方法被标记为 final,则无法使用 Cglib 进行动态代理。 + +2. **构造函数无法被代理** + + + Cglib 无法代理目标类的构造函数。因为构造函数的调用是在对象创建阶段完成的,而代理对象在目标对象创建后才生成,因此无法代理构造函数。 + +3. **内部类无法被代理** + + + Cglib 无法代理目标类中的内部类。这是因为 Cglib 是通过生成目标类的子类来实现代理,而内部类无法被继承。 \ No newline at end of file diff --git a/spring-aop/spring-aop-cglibProxy/src/main/java/com/xcs/spring/CglibProxyDemo.java b/spring-aop/spring-aop-cglibProxy/src/main/java/com/xcs/spring/CglibProxyDemo.java new file mode 100644 index 0000000..46ed40b --- /dev/null +++ b/spring-aop/spring-aop-cglibProxy/src/main/java/com/xcs/spring/CglibProxyDemo.java @@ -0,0 +1,21 @@ +package com.xcs.spring; + +import org.springframework.cglib.proxy.Enhancer; + +public class CglibProxyDemo { + + public static void main(String[] args) { + // 创建 Enhancer 对象,用于生成代理类 + Enhancer enhancer = new Enhancer(); + // 设置目标对象的父类 + enhancer.setSuperclass(MyServiceImpl.class); + // 设置回调拦截器 + enhancer.setCallback(new MyMethodInterceptor()); + // 创建代理对象 + MyService proxyObject = (MyService) enhancer.create(); + // 输出代理对象的类名 + System.out.println("ProxyObject = " + proxyObject.getClass()); + // 调用代理对象的方法 + proxyObject.doSomething(); + } +} diff --git a/spring-aop/spring-aop-cglibProxy/src/main/java/com/xcs/spring/MyMethodInterceptor.java b/spring-aop/spring-aop-cglibProxy/src/main/java/com/xcs/spring/MyMethodInterceptor.java new file mode 100644 index 0000000..62cefd2 --- /dev/null +++ b/spring-aop/spring-aop-cglibProxy/src/main/java/com/xcs/spring/MyMethodInterceptor.java @@ -0,0 +1,16 @@ +package com.xcs.spring; + +import org.springframework.cglib.proxy.MethodInterceptor; +import org.springframework.cglib.proxy.MethodProxy; + +import java.lang.reflect.Method; + +public class MyMethodInterceptor implements MethodInterceptor { + @Override + public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { + System.out.println("Before invoking method: " + method.getName()); + Object result = methodProxy.invokeSuper(obj, args); + System.out.println("After invoking method: " + method.getName()); + return result; + } +} diff --git a/spring-aop/spring-aop-cglibProxy/src/main/java/com/xcs/spring/MyService.java b/spring-aop/spring-aop-cglibProxy/src/main/java/com/xcs/spring/MyService.java new file mode 100644 index 0000000..d79d72d --- /dev/null +++ b/spring-aop/spring-aop-cglibProxy/src/main/java/com/xcs/spring/MyService.java @@ -0,0 +1,5 @@ +package com.xcs.spring; + +public interface MyService { + void doSomething(); +} diff --git a/spring-aop/spring-aop-cglibProxy/src/main/java/com/xcs/spring/MyServiceImpl.java b/spring-aop/spring-aop-cglibProxy/src/main/java/com/xcs/spring/MyServiceImpl.java new file mode 100644 index 0000000..e344775 --- /dev/null +++ b/spring-aop/spring-aop-cglibProxy/src/main/java/com/xcs/spring/MyServiceImpl.java @@ -0,0 +1,9 @@ +package com.xcs.spring; + +public class MyServiceImpl implements MyService { + + @Override + public void doSomething() { + System.out.println("hello world"); + } +}