AOP编程之cglib动态代理:进阶一

本文介绍了cglib实现动态代理的常识,如无法代理final修饰的类和方法,对比了cglib与JDK代理的区别。重点讲解了cglib动态代理的进阶内容,包括简单实现、代理类特点,还深入分析了拦截器实现逻辑,最后提出了cglib和JDK代理实现原理的问题。

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代理的实现原理?

转载于:https://www.cnblogs.com/outpointexception/p/10915254.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值