cglib实现动态代理的常识
1、无法代理final修饰的类和方法;
2、与JDK代理最大的区别是:
cglib动态代理:在编译后,通过修改字节码,生成新的代理对象proxyObj,此时该proxyObj是被代理类的子类;(关于字节码技术需要引入asm包,后续再深入研究)
jdk动态代理:通过反射机制实现动态代理,而且被代理的类必须是一个Interface的实现,也就是基于接口编程的;(更详细的了解,请参考文章:待补充)
在代理效率上,就能理解为什么cglib动态代理的效率为啥比jdk动态代理快;
本文的正题是cglib动态代理的进阶,就不再啰嗦这些肤浅的东西,进入正题:
1、首先让我们看看cglib的简单实现:
/** * 步骤1:被代理的类 UserDto */ public class UserDto { String name; String age; public UserDto(){ } public String getName() { return name; } public void setName(String name) { this.name = name; } // final 修饰的方法 public final String getAge() { return age; } public void setAge(String age) { this.age = age; } } /** * 步骤2:实现cglib拦截器 */ public class MyCglibInterceptor implements MethodInterceptor { public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("intercept:num123"); // if("void".equals(method.getGenericReturnType().toString())){ // methodProxy.invoke(o,objects); // return null; // } // return result = methodProxy.invoke(o,objects); return methodProxy.invokeSuper(o,objects); } } /** * 步骤3: 使用cglib动态代理 */ public class MainTest { public static void main(String[] args){ // cglib代理类class文件存入本地磁盘方便我们反编译查看源码 System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\cglibCode"); // 通过CGLIB动态代理获取代理对象的过程 Enhancer enhancer = new Enhancer(); // 设置enhancer对象的父类 enhancer.setSuperclass(UserDto.class); // 设置enhancer的回调对象 enhancer.setCallback(new MyCglibInterceptor()); // 创建代理对象 UserDto proxy= (UserDto)enhancer.create(); // 通过代理对象调用目标方法 proxy.setAge("123"); System.out.println(proxy.getAge()); } }
2、精彩的部分来了,通过反编译工具看看UserDto的cglib的代理类(关于如何配置反编译工具,请参考文章:待补充)
1 public class UserDto$$EnhancerByCGLIB$$cbf83845 extends UserDto implements Factory { 2 private boolean CGLIB$BOUND; 3 public static Object CGLIB$FACTORY_DATA; 4 private static final ThreadLocal CGLIB$THREAD_CALLBACKS; 5 private static final Callback[] CGLIB$STATIC_CALLBACKS; 6 private MethodInterceptor CGLIB$CALLBACK_0; 7 private static Object CGLIB$CALLBACK_FILTER; 8 private static final Method CGLIB$getName$0$Method; 9 private static final MethodProxy CGLIB$getName$0$Proxy; 10 private static final Object[] CGLIB$emptyArgs; 11 private static final Method CGLIB$setName$1$Method; 12 private static final MethodProxy CGLIB$setName$1$Proxy; 13 private static final Method CGLIB$setAge$2$Method; 14 private static final MethodProxy CGLIB$setAge$2$Proxy; 15 private static final Method CGLIB$equals$3$Method; 16 private static final MethodProxy CGLIB$equals$3$Proxy; 17 private static final Method CGLIB$toString$4$Method; 18 private static final MethodProxy CGLIB$toString$4$Proxy; 19 private static final Method CGLIB$hashCode$5$Method; 20 private static final MethodProxy CGLIB$hashCode$5$Proxy; 21 private static final Method CGLIB$clone$6$Method; 22 private static final MethodProxy CGLIB$clone$6$Proxy; 23 24 static void CGLIB$STATICHOOK1() { 25 CGLIB$THREAD_CALLBACKS = new ThreadLocal(); 26 CGLIB$emptyArgs = new Object[0]; 27 Class var0 = Class.forName("com.mj.app.proxy.UserDto$$EnhancerByCGLIB$$cbf83845"); 28 Class var1; 29 Method[] 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()); 30 CGLIB$equals$3$Method = var10000[0]; 31 CGLIB$equals$3$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$3"); 32 CGLIB$toString$4$Method = var10000[1]; 33 CGLIB$toString$4$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$4"); 34 CGLIB$hashCode$5$Method = var10000[2]; 35 CGLIB$hashCode$5$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$5"); 36 CGLIB$clone$6$Method = var10000[3]; 37 CGLIB$clone$6$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$6"); 38 var10000 = ReflectUtils.findMethods(new String[]{"getName", "()Ljava/lang/String;", "setName", "(Ljava/lang/String;)V", "setAge", "(Ljava/lang/String;)V"}, (var1 = Class.forName("com.mj.app.proxy.UserDto")).getDeclaredMethods()); 39 CGLIB$getName$0$Method = var10000[0]; 40 CGLIB$getName$0$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "getName", "CGLIB$getName$0"); 41 CGLIB$setName$1$Method = var10000[1]; 42 CGLIB$setName$1$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/String;)V", "setName", "CGLIB$setName$1"); 43 CGLIB$setAge$2$Method = var10000[2]; 44 CGLIB$setAge$2$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/String;)V", "setAge", "CGLIB$setAge$2"); 45 } 46 47 final String CGLIB$getName$0() { 48 return super.getName(); 49 } 50 51 public final String getName() { 52 MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; 53 if (this.CGLIB$CALLBACK_0 == null) { 54 CGLIB$BIND_CALLBACKS(this); 55 var10000 = this.CGLIB$CALLBACK_0; 56 } 57 58 return var10000 != null ? (String)var10000.intercept(this, CGLIB$getName$0$Method, CGLIB$emptyArgs, CGLIB$getName$0$Proxy) : super.getName(); 59 } 60 61 final void CGLIB$setName$1(String var1) { 62 super.setName(var1); 63 } 64 65 public final void setName(String var1) { 66 MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; 67 if (this.CGLIB$CALLBACK_0 == null) { 68 CGLIB$BIND_CALLBACKS(this); 69 var10000 = this.CGLIB$CALLBACK_0; 70 } 71 72 if (var10000 != null) { 73 var10000.intercept(this, CGLIB$setName$1$Method, new Object[]{var1}, CGLIB$setName$1$Proxy); 74 } else { 75 super.setName(var1); 76 } 77 } 78 79 final void CGLIB$setAge$2(String var1) { 80 super.setAge(var1); 81 } 82 83 public final void setAge(String var1) { 84 MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; 85 if (this.CGLIB$CALLBACK_0 == null) { 86 CGLIB$BIND_CALLBACKS(this); 87 var10000 = this.CGLIB$CALLBACK_0; 88 } 89 90 if (var10000 != null) { 91 var10000.intercept(this, CGLIB$setAge$2$Method, new Object[]{var1}, CGLIB$setAge$2$Proxy); 92 } else { 93 super.setAge(var1); 94 } 95 } 96 97 final boolean CGLIB$equals$3(Object var1) { 98 return super.equals(var1); 99 } 100 101 public final boolean equals(Object var1) { 102 MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; 103 if (this.CGLIB$CALLBACK_0 == null) { 104 CGLIB$BIND_CALLBACKS(this); 105 var10000 = this.CGLIB$CALLBACK_0; 106 } 107 108 if (var10000 != null) { 109 Object var2 = var10000.intercept(this, CGLIB$equals$3$Method, new Object[]{var1}, CGLIB$equals$3$Proxy); 110 return var2 == null ? false : (Boolean)var2; 111 } else { 112 return super.equals(var1); 113 } 114 } 115 116 final String CGLIB$toString$4() { 117 return super.toString(); 118 } 119 120 public final String toString() { 121 MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; 122 if (this.CGLIB$CALLBACK_0 == null) { 123 CGLIB$BIND_CALLBACKS(this); 124 var10000 = this.CGLIB$CALLBACK_0; 125 } 126 127 return var10000 != null ? (String)var10000.intercept(this, CGLIB$toString$4$Method, CGLIB$emptyArgs, CGLIB$toString$4$Proxy) : super.toString(); 128 } 129 130 final int CGLIB$hashCode$5() { 131 return super.hashCode(); 132 } 133 134 public final int hashCode() { 135 MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; 136 if (this.CGLIB$CALLBACK_0 == null) { 137 CGLIB$BIND_CALLBACKS(this); 138 var10000 = this.CGLIB$CALLBACK_0; 139 } 140 141 if (var10000 != null) { 142 Object var1 = var10000.intercept(this, CGLIB$hashCode$5$Method, CGLIB$emptyArgs, CGLIB$hashCode$5$Proxy); 143 return var1 == null ? 0 : ((Number)var1).intValue(); 144 } else { 145 return super.hashCode(); 146 } 147 } 148 149 final Object CGLIB$clone$6() throws CloneNotSupportedException { 150 return super.clone(); 151 } 152 153 protected final Object clone() throws CloneNotSupportedException { 154 MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; 155 if (this.CGLIB$CALLBACK_0 == null) { 156 CGLIB$BIND_CALLBACKS(this); 157 var10000 = this.CGLIB$CALLBACK_0; 158 } 159 160 return var10000 != null ? var10000.intercept(this, CGLIB$clone$6$Method, CGLIB$emptyArgs, CGLIB$clone$6$Proxy) : super.clone(); 161 } 162 163 public static MethodProxy CGLIB$findMethodProxy(Signature var0) { 164 String var10000 = var0.toString(); 165 switch(var10000.hashCode()) { 166 case -1184972270: 167 if (var10000.equals("setName(Ljava/lang/String;)V")) { 168 return CGLIB$setName$1$Proxy; 169 } 170 break; 171 case -508378822: 172 if (var10000.equals("clone()Ljava/lang/Object;")) { 173 return CGLIB$clone$6$Proxy; 174 } 175 break; 176 case -201623326: 177 if (var10000.equals("setAge(Ljava/lang/String;)V")) { 178 return CGLIB$setAge$2$Proxy; 179 } 180 break; 181 case 1218144844: 182 if (var10000.equals("getName()Ljava/lang/String;")) { 183 return CGLIB$getName$0$Proxy; 184 } 185 break; 186 case 1826985398: 187 if (var10000.equals("equals(Ljava/lang/Object;)Z")) { 188 return CGLIB$equals$3$Proxy; 189 } 190 break; 191 case 1913648695: 192 if (var10000.equals("toString()Ljava/lang/String;")) { 193 return CGLIB$toString$4$Proxy; 194 } 195 break; 196 case 1984935277: 197 if (var10000.equals("hashCode()I")) { 198 return CGLIB$hashCode$5$Proxy; 199 } 200 } 201 202 return null; 203 } 204 205 public UserDto$$EnhancerByCGLIB$$cbf83845() { 206 CGLIB$BIND_CALLBACKS(this); 207 } 208 209 public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] var0) { 210 CGLIB$THREAD_CALLBACKS.set(var0); 211 } 212 213 public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] var0) { 214 CGLIB$STATIC_CALLBACKS = var0; 215 } 216 217 private static final void CGLIB$BIND_CALLBACKS(Object var0) { 218 UserDto$$EnhancerByCGLIB$$cbf83845 var1 = (UserDto$$EnhancerByCGLIB$$cbf83845)var0; 219 if (!var1.CGLIB$BOUND) { 220 var1.CGLIB$BOUND = true; 221 Object var10000 = CGLIB$THREAD_CALLBACKS.get(); 222 if (var10000 == null) { 223 var10000 = CGLIB$STATIC_CALLBACKS; 224 if (CGLIB$STATIC_CALLBACKS == null) { 225 return; 226 } 227 } 228 229 var1.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])var10000)[0]; 230 } 231 232 } 233 234 public Object newInstance(Callback[] var1) { 235 CGLIB$SET_THREAD_CALLBACKS(var1); 236 UserDto$$EnhancerByCGLIB$$cbf83845 var10000 = new UserDto$$EnhancerByCGLIB$$cbf83845(); 237 CGLIB$SET_THREAD_CALLBACKS((Callback[])null); 238 return var10000; 239 } 240 241 public Object newInstance(Callback var1) { 242 CGLIB$SET_THREAD_CALLBACKS(new Callback[]{var1}); 243 UserDto$$EnhancerByCGLIB$$cbf83845 var10000 = new UserDto$$EnhancerByCGLIB$$cbf83845(); 244 CGLIB$SET_THREAD_CALLBACKS((Callback[])null); 245 return var10000; 246 } 247 248 public Object newInstance(Class[] var1, Object[] var2, Callback[] var3) { 249 CGLIB$SET_THREAD_CALLBACKS(var3); 250 UserDto$$EnhancerByCGLIB$$cbf83845 var10000 = new UserDto$$EnhancerByCGLIB$$cbf83845; 251 switch(var1.length) { 252 case 0: 253 var10000.<init>(); 254 CGLIB$SET_THREAD_CALLBACKS((Callback[])null); 255 return var10000; 256 default: 257 throw new IllegalArgumentException("Constructor not found"); 258 } 259 } 260 261 public Callback getCallback(int var1) { 262 CGLIB$BIND_CALLBACKS(this); 263 MethodInterceptor var10000; 264 switch(var1) { 265 case 0: 266 var10000 = this.CGLIB$CALLBACK_0; 267 break; 268 default: 269 var10000 = null; 270 } 271 272 return var10000; 273 } 274 275 public void setCallback(int var1, Callback var2) { 276 switch(var1) { 277 case 0: 278 this.CGLIB$CALLBACK_0 = (MethodInterceptor)var2; 279 default: 280 } 281 } 282 283 public Callback[] getCallbacks() { 284 CGLIB$BIND_CALLBACKS(this); 285 return new Callback[]{this.CGLIB$CALLBACK_0}; 286 } 287 288 public void setCallbacks(Callback[] var1) { 289 this.CGLIB$CALLBACK_0 = (MethodInterceptor)var1[0]; 290 } 291 292 static { 293 CGLIB$STATICHOOK1(); 294 } 295 }
从下面的代理类源码可知 :cglib的代理形式,实质是UserDto的子类,此外还有以下几个关键特点:
特点一:每个代理的方法都生成对应的两个方法,而且所有方法都是final类型;
// 直接调用父类的getName方法;
final String CGLIB$getName$0() { return super.getName(); } // 逻辑:先判断方法拦截器(就是上面刚定义的拦截器MyCglibInterceptor)是否有定义,如果没有,就直接调用父类的getName方法;所以在AOP编程中,也就可以在拦截其中实现before,after等等; public final String getName() { MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; if (this.CGLIB$CALLBACK_0 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_0; } return var10000 != null ? (String)var10000.intercept(this, CGLIB$getName$0$Method, CGLIB$emptyArgs, CGLIB$getName$0$Proxy) : super.getName(); }
特点二:UserDto中用final修饰的方法:public final void getAge()在代理类中找不到了,原因就是:final修饰的方法,无法被继承;
其它特点后续在补充;
接下来,我们再进一步窥探cglib动态代理拦截器实现逻辑:拦截器中的细节,就以 proxy.setAge("123");为例:
第一步:初始化并创建UserDto的增强代理类的实例proxy
// 通过CGLIB动态代理获取代理对象的过程 Enhancer enhancer = new Enhancer(); // 设置enhancer对象的父类 enhancer.setSuperclass(UserDto.class); // 设置enhancer的回调对象 enhancer.setCallback(new MyCglibInterceptor()); // 创建代理对象 UserDto proxy= (UserDto)enhancer.create();
第二步:执行 proxy.setAge("123");根据上面的分析,它实际是UserDto的子类的实例;所该方法的实现如下代码所示
public final void setAge(String var1) { MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; if (this.CGLIB$CALLBACK_0 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_0; } if (var10000 != null) {
// 如果有实现MethodIntercept接口,就会在此处调用你实现的拦截器的方法; var10000.intercept(this, CGLIB$setAge$2$Method, new Object[]{var1}, CGLIB$setAge$2$Proxy); } else { super.setAge(var1); } }
第三步:由于我们有实现MethodIntercept接口,var10000就是我们的拦截器MyCglibInterceptor的实例,接着通过执行var10000.intercept(...)进入我们的拦截器
public class MyCglibInterceptor implements MethodInterceptor { public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("intercept:num123"); // if("void".equals(method.getGenericReturnType().toString())){ // methodProxy.invoke(o,objects); // return null; // } // return result = methodProxy.invoke(o,objects); return methodProxy.invokeSuper(o,objects); } }
注意:1、springframework实现AOP的原理中,就在此时和aspectj进行集成,在拦截其中实现:before,after等等;
2、拦截器中的关键就是invoke(...),invokeSuper(..)
3、拦截器的参数解释:待补充
第四步:执行invoke,此时需要注意对invoke的实现由两种方式,1:invoke,2:invokeSuper;
第五步:最终通过:invokeSuper()调用被代理类UserDto的方法setAge;再逆向返回结果
最后执行完方法:proxy.setAge("123");
-------------------------------------------
初稿,后续完善
问题1: cglib如何通过修改字节码,为被代理类,创建超级代理类,该技术的实现原理?
问题2:jdk代理的实现原理?