动态代理

Posted Mr.袋鼠

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了动态代理相关的知识,希望对你有一定的参考价值。

这是动态原理的第二篇,这里要讲述的是Cglib的东东。接下来,进正题。

参考文章:http://www.cnblogs.com/cruze/p/3843996.html

 

一、Cglib

CGLIB是一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。通常可以使用Java的动态代理创建代理,但当要代理的类没有实现接口或者为了更好的性能,CGLIB是一个好的选择。

二、Cglib原理

动态生成一个要代理类的子类,子类重写要代理的类的所有不是final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。它比使用java反射的JDK动态代理要快。

三、实例分析

分析前,我用一句话来概括Cglib的过程。首先将被代理类TargetObject设置成父类,然后设置拦截器TargetInterceptor,最后执行enhancer.create()动态生成一个代理类,并从Object强制转型成父类型TargetObject。

1.demo注:示例代码我是从别的地方copy过来的,哪里来的我把地址忘了,所以没有贴上连接。但是后面的源码分析的部分,是自己一点点分析记录的。

1 package com.lee.demo.springSourceLearn.demo;
2 
3 public class BookEditImpl {  
4     public void addBook() {  
5         System.out.println("add Book...");  
6     }  
7 }  
 1 import java.lang.reflect.Method;
 2 
 3 import org.springframework.cglib.proxy.Enhancer;
 4 import org.springframework.cglib.proxy.MethodInterceptor;
 5 import org.springframework.cglib.proxy.MethodProxy;
 6 
 7 public class BookFacadeCglib implements MethodInterceptor {  
 8     private Object target;//业务类对象,供代理方法中进行真正的业务方法调用
 9   
10     //相当于JDK动态代理中的绑定
11     public Object getInstance(Object target) {  
12         this.target = target;  //给业务对象赋值
13         Enhancer enhancer = new Enhancer(); //创建加强器,用来创建动态代理类
14         enhancer.setSuperclass(this.target.getClass());  //为加强器指定要代理的业务类(即:为下面生成的代理类指定父类)
15         //设置回调:对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept()方法进行拦
16         enhancer.setCallback(this); 
17        // 创建动态代理类对象并返回  
18        return enhancer.create(); 
19     }
20     // 实现回调方法 
21     public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { 
22         System.out.println("预处理——————");
23         proxy.invokeSuper(obj, args); //调用业务类(父类中)的方法
24         System.out.println("调用后操作——————");
25         return null; 
26     } 
27 }
1 public class Test02 {
2 
3     public static void main(String[] args) {    
         //
生成的class文件默认只存储在内存中,我们可以在代码中加入下面语句来获取class file
         System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "C:\\\\Users\\\\lihao\\\\git");
4 BookEditImpl bookFacade=new BookEditImpl(); 5 BookFacadeCglib cglib=new BookFacadeCglib(); 6 BookEditImpl bookCglib=(BookEditImpl)cglib.getInstance(bookFacade); 7 bookCglib.addBook(); 8 } 9 }

运行执行结果如下:

预处理——————
add Book...
调用后操作——————

2.过程分析

首先getInstance中会先进行父类和回调方法的设置,接下来,进入enhancer.create的方法。

1     public Object create() {
         // 下面两行暂时不知道属性的作用,暂且放下,进入方法继续探索
2 this.classOnly = false; 3 this.argumentTypes = null; 4 return this.createHelper(); 5 }

 

 1 private Object createHelper() {
          //
进行有效性验证,比如有多个callBack却没有callBackFilter 2 this.validate(); 3 if (this.superclass != null) {
             // 前面已经设置了superClass,所以会进到这个分支里来
4 this.setNamePrefix(this.superclass.getName()); 5 } else if (this.interfaces != null) { 6 this.setNamePrefix(this.interfaces[ReflectUtils.findPackageProtected(this.interfaces)].getName()); 7 } 8
          // KEY_FACTORY根据代理类的组合信息,生成一个组合key,这个key用在后面的缓存的地方
// 为什么会用到缓存呢? 因为我们调用一个类中的某个方法,就会生成一个代理类,再调用类的另一个方法时,代理类已经存在了,就不要再重复创建了,因此便有了缓存的想法。
          // 父类: public class Enhancer extends AbstractClassGenerator
9 return super.create(KEY_FACTORY.newInstance(this.superclass != null ? this.superclass.getName() : null, 10 ReflectUtils.getNames(this.interfaces), this.filter, this.callbackTypes, this.useFactory, 11 this.interceptDuringConstruction, this.serialVersionUID)); 12 }

 

 1 protected Object create(Object key) {
 2         try {
 3             Class e = null;
 4             Source arg2 = this.source;
 5             synchronized (this.source) {
                 // 生成类的缓存是按照ClassLoader来区分的,因为类的区分是按照类名和ClassLoader两者组合来区分的,同样的类名,不同的ClassLodaer,那么类也是不同的
6 ClassLoader loader = this.getClassLoader(); 7 Object cache2 = null;
                 //
cache数据结构 Map<ClassLaodaer,Map<nameKey,HashSet>> 8 cache2 = (Map) this.source.cache.get(loader); 9 if (cache2 == null) { 10 cache2 = new HashMap(); 11 ((Map) cache2).put(NAME_KEY, new HashSet()); 12 this.source.cache.put(loader, cache2); 13 } else if (this.useCache) { 14 Reference save = (Reference) ((Map) cache2).get(key); 15 e = (Class) (save == null ? null : save.get()); 16 } 17 18 if (e == null) { 19 Object save1 = CURRENT.get(); 20 CURRENT.set(this); 21 22 Object b1; 23 try { 24 this.key = key;
// debug的时候发现这个
attemptLoad值为false,就不会进入这个分支,接下来进入到e=null里面 25 if (this.attemptLoad) { 26 try { 27 e = loader.loadClass(this.getClassName()); 28 } catch (ClassNotFoundException arg16) { 29 ; 30 } 31 } 32 33 if (e == null) {
                           // 代理类生成的操作,使用相应的策略生成
34 byte[] b = this.strategy.generate(this); 35 String className = ClassNameReader.getClassName(new ClassReader(b)); 36 this.getClassNameCache(loader).add(className); 37 e = ReflectUtils.defineClass(className, b, loader); 38 } 39 40 if (this.useCache) { 41 ((Map) cache2).put(key, new WeakReference(e)); 42 } 43 44 b1 = this.firstInstance(e); 45 } finally { 46 CURRENT.set(save1); 47 } 48 49 return b1; 50 } 51 } 52 53 return this.firstInstance(e); 54 } catch (RuntimeException arg19) { 55 throw arg19; 56 } catch (Error arg20) { 57 throw arg20; 58 } catch (Exception arg21) { 59 throw new CodeGenerationException(arg21); 60 } 61 }

下面代码一共三行,但是东西是真心不少啊!一行行分析。有时代码越少,越难

1 public byte[] generate(ClassGenerator cg) throws Exception {
2         DebuggingClassWriter cw = this.getClassVisitor();
3         this.transform(cg).generateClass(cw);
4         return this.transform(cw.toByteArray());
5     }
getClassVisitor
1 protected DebuggingClassWriter getClassVisitor() throws Exception {
2         return new DebuggingClassWriter(1);
3     }
DebuggingClassWriter 关注下面标红的部分
 1 public class DebuggingClassWriter extends ClassVisitor {
 2     public static final String DEBUG_LOCATION_PROPERTY = "cglib.debugLocation";
 3     private static String debugLocation = System.getProperty("cglib.debugLocation");
 4     private static Constructor traceCtor;
 5     private String className;
 6     private String superName;
 7 
 8     public DebuggingClassWriter(int flags) {
 9         super(262144, new ClassWriter(flags));
10     }
11 
12     public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
13         this.className = name.replace(\'/\', \'.\');
14         this.superName = superName.replace(\'/\', \'.\');
15         super.visit(version, access, name, signature, superName, interfaces);
16     }
17 
18     public String getClassName() {
19         return this.className;
20     }
21 
22     public String getSuperName() {
23         return this.superName;
24     }
25 
26     public byte[] toByteArray() {
27       return (byte[])((byte[])AccessController.doPrivileged(new 1(this)));
28    }
29 
30     static {
          // 通过这里就能知道,如何生成CGLIB动态代理类到本地
31 if (debugLocation != null) { 32 System.err.println("CGLIB debugging enabled, writing to \\\'" + debugLocation + "\\\'"); 33 34 try { 35 Class ignore = Class.forName("org.springframework.asm.util.TraceClassVisitor"); 36 traceCtor = ignore 37 .getConstructor(new Class[]{ 38 class$org$objectweb$asm$ClassVisitor == null 39 ? (class$org$objectweb$asm$ClassVisitor = class$( 40 "org.springframework.asm.ClassVisitor")) 41 : class$org$objectweb$asm$ClassVisitor, 42 class$java$io$PrintWriter == null 43 ? (class$java$io$PrintWriter = class$("java.io.PrintWriter")) 44 : class$java$io$PrintWriter}); 45 } catch (Throwable arg0) { 46 ; 47 } 48 } 49 50 } 51 }

回到generate方法,

1 protected ClassGenerator transform(ClassGenerator cg) throws Exception {
2         return cg;
3     }
1 public interface ClassGenerator {
2     void generateClass(ClassVisitor arg0) throws Exception;
3 }

那么,generateClass的实现类的方法是哪个呢?

public class Enhancer extends AbstractClassGenerator

public abstract class AbstractClassGenerator implements ClassGenerator

最后回归到Enhancer这个类中。。。

我们接下来回到上文create方法中的44行接着分析。(可能会有些跳跃性,因为有些地方无法一句句分析到,请谅解。写到这里为止,花了三个半小时,写过长的博客,也慢慢懂得前辈们优秀的文章中是包含了好多的心血,读起来,去理解消化也绝非是一蹴而就的事情。)

 b1 = this.firstInstance(e);

这一行调用了firstInstance方法,它是一个模板方法(空方法),在Enhancer类中实现了方法

 

1 protected Object firstInstance(Class type) throws Exception {
         // 在前文create方法中,将classOnly设置为false,因此进入到后面的分支
         // 建议简单的东西可以写三元运算符,复杂的东西不要用三元,因为一是容易出错,二是debug不方便
2 return this.classOnly ? type : this.createUsingReflection(type); 3 }

用反射创建实例,下面代码比较简单,大部分看名字就能知道其作用,我们着重看一下setThreadCallbacks方法,这里为了设置回调方法

 1 private Object createUsingReflection(Class type) {
 2         setThreadCallbacks(type, this.callbacks);
 3 
 4         Object arg1;
 5         try {
 6             if (this.argumentTypes != null) {
 7                 arg1 = ReflectUtils.newInstance(type, this.argumentTypes, this.arguments);
 8                 return arg1;
 9             }
10 
11             arg1 = ReflectUtils.newInstance(type);
12         } finally {
13             setThreadCallbacks(type, (Callback[]) null);
14         }
15 
16         return arg1;
17     }

 

 1 private static void setThreadCallbacks(Class type, Callback[] callbacks) {
 2         setCallbacksHelper(type, callbacks, "CGLIB$SET_THREAD_CALLBACKS");
 3     }
 4 
 5 
 6 private static void setCallbacksHelper(Class type, Callback[] callbacks, String methodName) {
 7         try {
 8             Method e = getCallbacksSetter(type, methodName);
 9             e.invoke((Object) null, new Object[]{callbacks});
10         } catch (NoSuchMethodException arg3) {
11             throw new IllegalArgumentException(type + " is not an enhanced class");
12         } catch (IllegalAccessException arg4) {
13             throw new CodeGenerationException(arg4);
14         } catch (InvocationTargetException arg5) {
15             throw new CodeGenerationException(arg5);
16         }
17     }

这个方法中的内容,不明白,如果有明白的朋友,请您告诉我。!!!

1 private static Method getCallbacksSetter(Class type, String methodName) throws NoSuchMethodException {
2         return type
3                 .getDeclaredMethod(methodName,
4                         new Class[]{array$Lnet$sf$cglib$proxy$Callback == null
5                                 ? (array$Lnet$sf$cglib$proxy$Callback = class$(
6                                         "[Lorg.springframework.cglib.proxy.Callback;"))
7                                 : array$Lnet$sf$cglib$proxy$Callback});
8     }

暂且写到这里了,自己感觉后部分,有许多东西,写的不透彻,原因是自己也没有百分百理解好,导致了这个现象,惭愧!!!

继续努力,fighting!!!

 

 

 

以上是关于动态代理的主要内容,如果未能解决你的问题,请参考以下文章

动态 Rstudio 代码片段

是否可以动态编译和执行 C# 代码片段?

支持动态或静态片段的不同屏幕尺寸?

Forge Viewer - 如何在场景中访问(或获取渲染/片段代理)克隆的网格?

在ansible模板中使用动态组名称

代理模式(动态)