一. 前言
虽然平时日常开发很少用到动态代理, 但是动态代理在底层框架等有着非常重要的意义. 比如 Spring AOP 使用 cglib 和 JDK 动态代理, Hibernate 底层使用了 javassit 和 cglib 动态代理, Dubbo 使用 javassist 字节码 (具体可以看 Dubbo SPI).
本文主要介绍什么是动态代理及原理, 下文将介绍 Spring AOP
我们先思考一个问题: 如何统计一个类各个方法的执行时间? 可能你心里有好多答案都可以解决问题.
那么如果是这个项目的多个不同类呢? 可能心里也有答案, 但是代码改动量不少. 那么有什么其他的方法么?
这时候动态代理就出来了, 它可以灵活的在方法, 代码点上切入我们想要实现的逻辑. 如图示:
二. 体验动态代理
2.1 JDK 动态代理
在 Java 的 java.lang.reflect 包下提供了 Proxy 类和 InvocationHandler 接口, 通过使用这两个类可以生成 JDK 动态代理类或者 JDK 动态代理对象
JDK 动态代理只能针对实现了接口的类进行拓展, 我们还是就上面的问题来结局. 所以这里我们先创建一个接口, 叫 Developer(开发), 里面有个方法是 develop 方法. 有 RookieDeveloper(新生开发),PrimaryDeveloper(初级开发) 和 AdvancedDeveloper(高级开发) 实现其接口. 类图如下:
我现在对 AdvancedDeveloper 进行动态代理, 先来看一下 AdvancedDeveloper 的代码:
接下来请看如何使用 Proxy 和 InvocationHandler 生成动态代理的:
运行结果如下:
2.2 CGLib 动态代理
CGLIB 代理的核心是 net.sf.cglib.proxy.Enhancer 类. 我们可以将自定义的 net.sf.cglib.proxy.MethodInterceptor 实现类来得到强大的代理. 代理的所有方法调用都会被分派给 net.sf.cglib.proxy.MethodInterceptor 的 intercept 方法. intercept 方法然后调用底层对象.
我们看一下 Cglib 动态代理的例子, 先看下 PrimaryDeveloper 类:
再看下 CGLib 动态代理测试类:
简而言之, proxy.invoke 方法调用的对象不是代理后的子类, proxy.invokeSuper 方法调用的对象是代理后的子类 (已增强), 所以会再走一遍 MyMethodInterceptor 的 interceptor 方法, 如果是个拦截器链条, 就会重新在走一次拦截器链; 最后看一下执行结果:
三. 动态代理原理
3.1 JDK 动态代理
我们首先看一下 java.lang.reflect.Proxy#newProxyInstance 这个方法:
这里还有一个关键点, 在 java.lang.reflect.Proxy.ProxyClassFactory#apply 方法里, 有一段代码生产对应的 class 字节码文件:
简单总结一下上面的代码:
生成一个实现 interfaces 所有接口且继承 Proxy 类的代理类
使用 Proxy(InvocationHandler h) 构造一个代理类实例
传入我们定义的 InvocationHandler(例子中是匿名内部类), 构造器实例化了代理对象
最后我们看一下生成的类的代码, 我使用的是 Bytecode Viewer,GitHub 地址: https://github.com/Konloch/bytecode-viewer . 我们用 debug evaluate 获取到代理类的 class 文件, 然后用 Bytecode Viewer 瞅瞅是啥样子:
生产类的代码出来啦, 继承 Proxy 类, 实现 Developer 接口, 调用所有方法都转换成了实际调用 InvocationHandler 接口的 invoke 方法:
3.2 CGLib 动态代理
我们从生成的动态代理类长啥样开始研究. 上面的例子, 添加 System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/Users/miaojiaxing/Downloads"); 后运行会生成几个. class 文件:
PrimaryDeveloper$$EnhancerByCGLIB$$bda772e0:CGLib 生成的代理类
PrimaryDeveloper$$EnhancerByCGLIB$$bda772e0$$FastClassByCGLIB$$aeaf1210: 代理类的 FastClass
PrimaryDeveloper$$FastClassByCGLIB$$de1a7774: 被代理类的 FastClass(有点绕口)
首先用反编译工具查看一下 PrimaryDeveloper$$EnhancerByCGLIB$$bda772e0 这个类的代码:
- package com.mjx.java.proxy;
- import java.lang.reflect.Method;
- import.NET.sf.cglib.core.ReflectUtils;
- import.NET.sf.cglib.core.Signature;
- import.NET.sf.cglib.proxy.Callback;
- import.NET.sf.cglib.proxy.Factory;
- import.NET.sf.cglib.proxy.MethodInterceptor;
- import.NET.sf.cglib.proxy.MethodProxy;
- public class PrimaryDeveloper$$EnhancerByCGLIB$$bda772e0 extends PrimaryDeveloper 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;
- // 代理类会获得所有在父类继承来的方法, 并且会有 MethodProxy 与之对应
- private static final Method CGLIB$laugh$0$Method;
- private static final MethodProxy CGLIB$laugh$0$Proxy;
- private static final Object[] CGLIB$emptyArgs;
- // 代理类会获得所有在父类继承来的方法, 并且会有 MethodProxy 与之对应
- private static final Method CGLIB$develop$1$Method;
- private static final MethodProxy CGLIB$develop$1$Proxy;
- private static final Method CGLIB$say$2$Method;
- private static final MethodProxy CGLIB$say$2$Proxy;
- private static final Method CGLIB$equals$3$Method;
- private static final MethodProxy CGLIB$equals$3$Proxy;
- private static final Method CGLIB$toString$4$Method;
- private static final MethodProxy CGLIB$toString$4$Proxy;
- private static final Method CGLIB$hashCode$5$Method;
- private static final MethodProxy CGLIB$hashCode$5$Proxy;
- private static final Method CGLIB$clone$6$Method;
- private static final MethodProxy CGLIB$clone$6$Proxy;
- static void CGLIB$STATICHOOK1() {
- CGLIB$THREAD_CALLBACKS = new ThreadLocal();
- CGLIB$emptyArgs = new Object[0];
- Class var0 = Class.forName("com.mjx.java.proxy.PrimaryDeveloper$$EnhancerByCGLIB$$bda772e0");
- Class var1;
- Method[] var10000 = ReflectUtils.findMethods(new String[]{"laugh", "()V", "develop", "()V", "say", "()V"}, (var1 = Class.forName("com.mjx.java.proxy.PrimaryDeveloper")).getDeclaredMethods());
- CGLIB$laugh$0$Method = var10000[0];
- CGLIB$laugh$0$Proxy = MethodProxy.create(var1, var0, "()V", "laugh", "CGLIB$laugh$0");
- CGLIB$develop$1$Method = var10000[1];
- CGLIB$develop$1$Proxy = MethodProxy.create(var1, var0, "()V", "develop", "CGLIB$develop$1");
- CGLIB$say$2$Method = var10000[2];
- CGLIB$say$2$Proxy = MethodProxy.create(var1, var0, "()V", "say", "CGLIB$say$2");
- var10000 = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
- CGLIB$equals$3$Method = var10000[0];
- CGLIB$equals$3$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$3");
- CGLIB$toString$4$Method = var10000[1];
- CGLIB$toString$4$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$4");
- CGLIB$hashCode$5$Method = var10000[2];
- CGLIB$hashCode$5$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$5");
- CGLIB$clone$6$Method = var10000[3];
- CGLIB$clone$6$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$6");
- }
- final void CGLIB$laugh$0() {
- super.laugh();
- }
- public final void laugh() {
- MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
- if (var10000 == null) {
- CGLIB$BIND_CALLBACKS(this);
- var10000 = this.CGLIB$CALLBACK_0;
- }
- if (var10000 != null) {
- // 拦截器
- var10000.intercept(this, CGLIB$laugh$0$Method, CGLIB$emptyArgs, CGLIB$laugh$0$Proxy);
- } else {
- super.laugh();
- }
- }
- // methodProxy.invokeSuper 会调用
- final void CGLIB$develop$1() throws Exception {
- super.develop();
- }
- public final void develop() throws Exception {
- MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
- if (var10000 == null) {
- CGLIB$BIND_CALLBACKS(this);
- var10000 = this.CGLIB$CALLBACK_0;
- }
- // 拦截
- if (var10000 != null) {
- var10000.intercept(this, CGLIB$develop$1$Method, CGLIB$emptyArgs, CGLIB$develop$1$Proxy);
- } else {
- super.develop();
- }
- }
我们可以看到代理类会获得所有在父类继承来的方法, 并且会有 MethodProxy 与之对应, 这个非常关键, 上面已经用红色标注.
- 3.2.1 MethodProxy
- CGLIB$develop$1$Proxy = MethodProxy.create(var1, var0, "()V", "develop", "CGLIB$develop$1");
我们先看下创建 MethodProxy:
- public class MethodProxy {
- private Signature sig1;
- private Signature sig2;
- private CreateInfo createInfo;
- private final Object initLock = new Object();
- private volatile FastClassInfo fastClassInfo;
- /**
- * For internal use by {@link Enhancer} only; see the {@link.NET.sf.cglib.reflect.FastMethod} class
- * for similar functionality.
- */
- // c1: 被代理对象 Class
- // c2: 代理对象 Class
- // desc: 入参类型
- // name1: 被代理方法名
- // name2: 代理方法名
- public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
- MethodProxy proxy = new MethodProxy();
- proxy.sig1 = new Signature(name1, desc);
- proxy.sig2 = new Signature(name2, desc);
- proxy.createInfo = new CreateInfo(c1, c2);
- return proxy;
- }
- private static class CreateInfo
- {
- Class c1;
- Class c2;
- NamingPolicy namingPolicy;
- GeneratorStrategy strategy;
- boolean attemptLoad;
- public CreateInfo(Class c1, Class c2)
- {
- this.c1 = c1;
- this.c2 = c2;
- AbstractClassGenerator fromEnhancer = AbstractClassGenerator.getCurrent();
- if (fromEnhancer != null) {
- namingPolicy = fromEnhancer.getNamingPolicy();
- strategy = fromEnhancer.getStrategy();
- attemptLoad = fromEnhancer.getAttemptLoad();
- }
- }
- }
- }
创建代理之后, 执行方法这里走到红色的代码: var10000.intercept(this, CGLIB$develop$1$Method, CGLIB$emptyArgs, CGLIB$develop$1$Proxy); 走到了我们写的 CglibInterceptor 类的 intercept 方法, 里面调用了 proxy.invokeSuper(obj,args);
- public Object invokeSuper(Object obj, Object[] args) throws Throwable {
- try {
- init();
- FastClassInfo fci = fastClassInfo;
- // 这里的 f2 就是 PrimaryDeveloper$$EnhancerByCGLIB$$bda772e0$$FastClassByCGLIB$$aeaf1210
- return fci.f2.invoke(fci.i2, obj, args);
- } catch (InvocationTargetException e) {
- throw e.getTargetException();
- }
- }
- private static class FastClassInfo{
- FastClass f1;// 被代理类 FastClass, 这里就是 PrimaryDeveloper$$FastClassByCGLIB$$de1a7774
- FastClass f2;// 代理类 FastClass, 这里就是 PrimaryDeveloper$$EnhancerByCGLIB$$bda772e0$$FastClassByCGLIB$$aeaf1210
- int i1;// 被代理方法 index
- int i2;// 代理方法 index
- }
3.2.1 FastClass 机制
Cglib 动态代理执行代理方法效率之所以比 JDK 的高是因为 Cglib 采用了 FastClass 机制, 它为代理类和被代理类各生成一个 Class(就是上面的 f1 和 f2), 它会为代理类或被代理类的方法分配一个 index(int 类型). 这个 index 是用签名的 hashCode 来计算出来的 Index(下面代码有),FastClass 就可以直接定位要调用的方法直接进行调用, 那么就省去了反射, 所以调用效率比 JDK 动态代理通过反射调用高. f2 我们已经知道是 PrimaryDeveloper$$EnhancerByCGLIB$$bda772e0$$FastClassByCGLIB$$aeaf1210, 下面我们反编译一下 f2 看看:
继续看下 f2 的 invoke 方法, 直接调用 PrimaryDeveloper$$EnhancerByCGLIB$$bda772e0 的方法, 无需反射.
最后我们整理一下调用过程, 这样比较清晰了吧, 如图:
四. 总结
JDK 动态代理:
首先这个类要实现了某接口
其核心就是克隆 interfaces 的所有接口, 继承 Proxy 类, 生成一个心类作为代理类, 这个类里有我们定义的实现 InvocationHandler 接口的类进行代理逻辑处理
CGLib 动态代理:
JDK 动态代理有个重大缺陷, 必须要实现接口才可以使用, 而 CGLib 动态代理只要有个类就行, 动态生成子类. 如果是 private 方法, final 方法等描述的方法是不能被代理的
Cglib 动态代理执行代理方法效率之所以比 JDK 的高是因为 Cglib 采用了 FastClass 机制
来源: https://www.cnblogs.com/GrimMjx/p/11194283.html