cglib源码分析(一): 缓存和KEY

    cglib是一个java 字节码的生成工具,它是对asm的进一步封装,提供了一系列class generator。研究cglib主要是因为它也提供了动态代理功能,这点和jdk的动态代理类似。

一、 Cache的创建

    与jdk动态代理一样,cglib也提供了缓存来提高系统的性能,对于已经生成的类,直接使用而不必重复生成。这里不得不提到一个比较重要的抽象类AbstractClassGenerator,它采用了模版方法的设计模式,protected Object create(Object key) 就是模版方法,它定义了类生成的过程。AbstractClassGenerator只有一个构造函数protected AbstractClassGenerator(Source source),入参是一个Source类型的对象,Source是AbstractClassGenerator里面的一个静态内部类,Source有两个字段 name用来记录class generator,cache 就是缓存,它和jdk动态代理一样都是用了WeakHashMap,并且类型也是<ClassLoader,<Object,Class>>

    protected static class Source {
        String name; //class generator的name,eg:如果使用Enhancer来生成增强类,name的值就为 net.sf.cglib.proxy. Enhancer
        Map cache = new WeakHashMap();
        public Source(String name) {
            this.name = name;
        }
}

        每个class generator都必须继承AbstractClassGenerator并且实现 public void generateClass(ClassVisitor v) 方法用来生成所需要的类。每个class generator都有独立的缓存,比如 Enhancer 类中 private static final Source SOURCE = new Source(Enhancer.class.getName()); 在BeanGenerator 中 private static final Source SOURCE = new Source(BeanGenerator.class.getName()); 

二、 Cache的使用

    缓存的使用主要封装在AbstractClassGenerator的模版方法create中,下面是create方法的源码:

synchronized (source) {
                ClassLoader loader = getClassLoader();
                Map cache2 = null;
                cache2 = (Map)source.cache.get(loader);
                if (cache2 == null) {
                    cache2 = new HashMap();
                    cache2.put(NAME_KEY, new HashSet());
                    source.cache.put(loader, cache2);
                } else if (useCache) {
                    Reference ref = (Reference)cache2.get(key);
                    gen = (Class) (( ref == null ) ? null : ref.get()); 
                }
…… 忽略若干代码
                if (useCache) {
                     cache2.put(key, new WeakReference(gen));
            }
}

    生成类的缓存是按照ClassLoader来划分的,这是因为类的区分不仅根据类名还根据装载类的ClassLoader,也就是说同一个类被不同的ClassLoader加载,那么它们也是不同的,关于这部分内容可参考 http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-5.html#jvms-5.3 。每个ClassLoader的缓存中都会有一个NAME_KEY 这个主要是用来对生成的class name进行去重,NAME_KEY 会在生成类命名策略里有进一步的说明。此处还使用useCache 来标记是否使用缓存,这给了用户比较灵活的选择。

三、Key的例子

    每个生成类在缓存中都会有一个key与之相对应。对于那些只与单个类相关的生成类,可以采用类名作为key。在动态代理中生成类不仅与目标类相关,还与使用的拦截类(MethodInterceptor),过滤类(CallbackFilter)相关,这样的话就要使用multi-vaules key来标识这个生成类,在cglib中multi-vaules 也是动态生成的,KeyFactory  就是生成multi-vaules的工厂类,它是一个抽象类,也就是说它不能被实例化,但是它提供了一系列的静态工厂方法来生成multi-vaules的工厂类,这里很拗口,下面是cglib源码包中的一个例子:

package samples;
import net.sf.cglib.core.KeyFactory;
public class KeySample {
    private interface MyFactory {
        public Object newInstance(int a, char[] b, String d);
    }
    public static void main(String[] args) {
        MyFactory f = (MyFactory)KeyFactory.create(MyFactory.class);
        Object key1 = f.newInstance(20, new char[]{ 'a', 'b' }, "hello");
        Object key2 = f.newInstance(20, new char[]{ 'a', 'b' }, "hello");
        Object key3 = f.newInstance(20, new char[]{ 'a', '_' }, "hello");
        System.out.println(key1.equals(key2));
        System.out.println(key1.toString());
        System.out.println(key2.equals(key3));
    }
}

 

运行结果是:

true
20, {a, b}, hello
false

 

      为了生成multi-vaules 的工厂类,我们必须提供一个接口来描述multi-vaules的结构(上例中该接口为MyFactory),该接口有且只有一个方法newInstance,该方法的返回值必须为Object,该方法的入参可以是任意的对象,元数据类型 或者是任意维的数组 但是入参不能为空,如果为空就和默认的构造函数相冲突。 在分析KeyFactory之前,我们将上例生成的multi-vaules工厂类进行一下反编译(jd-gui 由于版本的问题无法反编译,因而此处使用javap):

public class samples.KeySample$MyFactory$$KeyFactoryByCGLIB$$7116a61e extends net.sf.cglib.core.KeyFactory implements samples.KeySample$MyFactory{
public samples.KeySample$MyFactory$$KeyFactoryByCGLIB$$7116a61e();
  Code:
   0:   aload_0
   1:   invokespecial   #11; //Method net/sf/cglib/core/KeyFactory."<init>":()V
   4:   return

public java.lang.Object newInstance(int, char[], java.lang.String);
  Code:
   0:   new     #2; //class samples/KeySample$MyFactory$$KeyFactoryByCGLIB$$7116a61e
   3:   dup
   4:   iload_1
   5:   aload_2
   6:   aload_3
   7:   invokespecial   #16; //Method "<init>":(I[CLjava/lang/String;)V
   10:  areturn

public samples.KeySample$MyFactory$$KeyFactoryByCGLIB$$7116a61e(int, char[], java.lang.String);
  Code:
   0:   aload_0
   1:   invokespecial   #11; //Method net/sf/cglib/core/KeyFactory."<init>":()V
   4:   aload_0
   5:   dup
   6:   iload_1
   7:   putfield        #20; //Field FIELD_0:I
   10:  dup
   11:  aload_2
   12:  putfield        #24; //Field FIELD_1:[C
   15:  dup
   16:  aload_3
   17:  putfield        #28; //Field FIELD_2:Ljava/lang/String;
   20:  return

public int hashCode();
  Code:
   0:   sipush  179
   3:   aload_0
   4:   getfield        #20; //Field FIELD_0:I
   7:   swap
   8:   ldc     #31; //int 467063
   10:  imul
   11:  swap
   12:  iadd
   13:  aload_0
   14:  getfield        #24; //Field FIELD_1:[C
   17:  dup
   18:  ifnull  48
   21:  astore_1
   22:  iconst_0
   23:  istore_2
   24:  goto    39
   27:  aload_1
   28:  iload_2
   29:  caload
   30:  swap
   31:  ldc     #31; //int 467063
   33:  imul
   34:  swap
   35:  iadd
   36:  iinc    2, 1
   39:  iload_2
   40:  aload_1
   41:  arraylength
   42:  if_icmplt       27
   45:  goto    49
   48:  pop
   49:  aload_0
   50:  getfield        #28; //Field FIELD_2:Ljava/lang/String;
   53:  swap
   54:  ldc     #31; //int 467063
   56:  imul
   57:  swap
   58:  dup
   59:  ifnull  68
   62:  invokevirtual   #35; //Method java/lang/Object.hashCode:()I
   65:  goto    70
   68:  pop
   69:  iconst_0
   70:  iadd
   71:  ireturn

public boolean equals(java.lang.Object);
  Code:
   0:   aload_1
   1:   instanceof      #2; //class samples/KeySample$MyFactory$$KeyFactoryByCGLIB$$7116a61e
   4:   ifeq    133
   7:   aload_0
   8:   getfield        #20; //Field FIELD_0:I
   11:  aload_1
   12:  checkcast       #2; //class samples/KeySample$MyFactory$$KeyFactoryByCGLIB$$7116a61e
   15:  getfield        #20; //Field FIELD_0:I
   18:  if_icmpne       133
   21:  aload_0
   22:  getfield        #24; //Field FIELD_1:[C
   25:  aload_1
   26:  checkcast       #2; //class samples/KeySample$MyFactory$$KeyFactoryByCGLIB$$7116a61e
   29:  getfield        #24; //Field FIELD_1:[C
   32:  dup2
   33:  ifnonnull       43
   36:  ifnonnull       49
   39:  pop2
   40:  goto    93
   43:  ifnull  49
   46:  goto    53
   49:  pop2
   50:  goto    133
   53:  dup2
   54:  arraylength
   55:  swap
   56:  arraylength
   57:  if_icmpeq       64
   60:  pop2
   61:  goto    133
   64:  astore_2
   65:  astore_3
   66:  iconst_0
   67:  istore  4
   69:  goto    86
   72:  aload_2
   73:  iload   4
   75:  caload
   76:  aload_3
   77:  iload   4
   79:  caload
   80:  if_icmpne       133
   83:  iinc    4, 1
   86:  iload   4
   88:  aload_2
   89:  arraylength
   90:  if_icmplt       72
   93:  aload_0
   94:  getfield        #28; //Field FIELD_2:Ljava/lang/String;
   97:  aload_1
   98:  checkcast       #2; //class samples/KeySample$MyFactory$$KeyFactoryByCGLIB$$7116a61e
   101: getfield        #28; //Field FIELD_2:Ljava/lang/String;
   104: dup2
   105: ifnonnull       115
   108: ifnonnull       121
   111: pop2
   112: goto    131
   115: ifnull  121
   118: goto    125
   121: pop2
   122: goto    133
   125: invokevirtual   #39; //Method java/lang/Object.equals:(Ljava/lang/Object;)Z
   128: ifeq    133
   131: iconst_1
   132: ireturn
   133: iconst_0
   134: ireturn

public java.lang.String toString();
  Code:
   0:   new     #43; //class java/lang/StringBuffer
   3:   dup
   4:   invokespecial   #44; //Method java/lang/StringBuffer."<init>":()V
   7:   aload_0
   8:   getfield        #20; //Field FIELD_0:I
   11:  invokevirtual   #48; //Method java/lang/StringBuffer.append:(I)Ljava/lang/StringBuffer;
   14:  goto    23
   17:  pop
   18:  ldc     #50; //String null
   20:  invokevirtual   #53; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
   23:  ldc     #55; //String ,
   25:  invokevirtual   #53; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
   28:  aload_0
   29:  getfield        #24; //Field FIELD_1:[C
   32:  dup
   33:  ifnull  96
   36:  swap
   37:  ldc     #57; //String {
   39:  invokevirtual   #53; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
   42:  swap
   43:  astore_1
   44:  iconst_0
   45:  istore_2
   46:  goto    72
   49:  aload_1
   50:  iload_2
   51:  caload
   52:  invokevirtual   #60; //Method java/lang/StringBuffer.append:(C)Ljava/lang/StringBuffer;
   55:  goto    64
   58:  pop
   59:  ldc     #50; //String null
   61:  invokevirtual   #53; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
   64:  ldc     #55; //String ,
   66:  invokevirtual   #53; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
   69:  iinc    2, 1
   72:  iload_2
   73:  aload_1
   74:  arraylength
   75:  if_icmplt       49
   78:  dup
   79:  dup
   80:  invokevirtual   #63; //Method java/lang/StringBuffer.length:()I
   83:  iconst_2
   84:  isub
   85:  invokevirtual   #67; //Method java/lang/StringBuffer.setLength:(I)V
   88:  ldc     #69; //String }
   90:  invokevirtual   #53; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
   93:  goto    102
   96:  pop
   97:  ldc     #50; //String null
   99:  invokevirtual   #53; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
   102: ldc     #55; //String ,
   104: invokevirtual   #53; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
   107: aload_0
   108: getfield        #28; //Field FIELD_2:Ljava/lang/String;
   111: dup
   112: ifnull  124
   115: invokevirtual   #71; //Method java/lang/Object.toString:()Ljava/lang/String;
   118: invokevirtual   #53; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
   121: goto    130
   124: pop
   125: ldc     #50; //String null
   127: invokevirtual   #53; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
   130: invokevirtual   #72; //Method java/lang/StringBuffer.toString:()Ljava/lang/String;
   133: areturn

}
View Code

 

从反编译的结果我们可以看出,生成的工厂类为samples.KeySample$MyFactory$$KeyFactoryByCGLIB$$7116a61e,它继承了net.sf.cglib.core.KeyFactory 类,实现了samples.KeySample$MyFactory接口,同时也实现了工厂方法newInstance,所以上例中key1,key2,key3都是通过该工厂方法产生了key的对象。samples.KeySample$MyFactory$$KeyFactoryByCGLIB$$7116a61e 中有两个构造函数,一个是默认的构造函数(由于工厂方法newInstance 为非静态方法,所以需要使用默认构造函数来生成第一个对象),另外一个构造函数的入参和 newInstance 入参一样,newInstance使用有参构造函数来生成multi-values key 对象。samples.KeySample$MyFactory$$KeyFactoryByCGLIB$$7116a61e 为newInstance的每一个入参都生成了一个相同类型的field用来存储key的value。除此之外还提供了三个方法:hashCode, equals, toString.

hashCode 和 equals 在HashMap 的get方法中用于和存储的key进行比较,所以这两个方法是比较重要的。

四、KeyFactory 源码分析

KeyFactory的源码还是比较多的,接下来只对其中的关键代码进行分析:

        public void generateClass(ClassVisitor v) {
            ClassEmitter ce = new ClassEmitter(v);
            //对定义key工厂类结构的接口进行判断,判断该接口是否只有newInstance一个方法,newInstance的返回值是否为Object
            Method newInstance = ReflectUtils.findNewInstance(keyInterface);
            if (!newInstance.getReturnType().equals(Object.class)) {
                throw new IllegalArgumentException("newInstance method must return Object");
            }

            //获取newInstance的入参类型,此处使用ASM的Type来定义
            Type[] parameterTypes = TypeUtils.getTypes(newInstance.getParameterTypes());
            ce.begin_class(Constants.V1_2,
                           Constants.ACC_PUBLIC,
                           getClassName(),
                           KEY_FACTORY,
                           new Type[]{ Type.getType(keyInterface) },
                           Constants.SOURCE_FILE);
            //生成默认构造函数
            EmitUtils.null_constructor(ce);
            
            //生成newInstance 工厂方法
            EmitUtils.factory_method(ce, ReflectUtils.getSignature(newInstance));

            //生成有参构造方法
            int seed = 0;
            CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC,
                                            TypeUtils.parseConstructor(parameterTypes),
                                            null);
            e.load_this();
            e.super_invoke_constructor();
            e.load_this();
            for (int i = 0; i < parameterTypes.length; i++) {
                seed += parameterTypes[i].hashCode();
                //为每一个入参生成一个相同类型的类字段
                ce.declare_field(Constants.ACC_PRIVATE | Constants.ACC_FINAL,
                                 getFieldName(i),
                                 parameterTypes[i],
                                 null);
                e.dup();
                e.load_arg(i);
                e.putfield(getFieldName(i));
            }
            e.return_value();
            e.end_method();
            
            //生成hashCode
            e = ce.begin_method(Constants.ACC_PUBLIC, HASH_CODE, null);
            int hc = (constant != 0) ? constant : PRIMES[(int)(Math.abs(seed) % PRIMES.length)];
            int hm = (multiplier != 0) ? multiplier : PRIMES[(int)(Math.abs(seed * 13) % PRIMES.length)];
            e.push(hc);
            for (int i = 0; i < parameterTypes.length; i++) {
                e.load_this();
                e.getfield(getFieldName(i));
                EmitUtils.hash_code(e, parameterTypes[i], hm, customizer);
            }
            e.return_value();
            e.end_method();

            //生成equals函数,在equals函数中对每个入参都进行判断
            e = ce.begin_method(Constants.ACC_PUBLIC, EQUALS, null);
            Label fail = e.make_label();
            e.load_arg(0);
            e.instance_of_this();
            e.if_jump(e.EQ, fail);
            for (int i = 0; i < parameterTypes.length; i++) {
                e.load_this();
                e.getfield(getFieldName(i));
                e.load_arg(0);
                e.checkcast_this();
                e.getfield(getFieldName(i));
                EmitUtils.not_equals(e, parameterTypes[i], fail, customizer);
            }
            e.push(1);
            e.return_value();
            e.mark(fail);
            e.push(0);
            e.return_value();
            e.end_method();

            //生成toString方法
            e = ce.begin_method(Constants.ACC_PUBLIC, TO_STRING, null);
            e.new_instance(Constants.TYPE_STRING_BUFFER);
            e.dup();
            e.invoke_constructor(Constants.TYPE_STRING_BUFFER);
            for (int i = 0; i < parameterTypes.length; i++) {
                if (i > 0) {
                    e.push(", ");
                    e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_STRING);
                }
                e.load_this();
                e.getfield(getFieldName(i));
                EmitUtils.append_string(e, parameterTypes[i], EmitUtils.DEFAULT_DELIMITERS, customizer);
            }
            e.invoke_virtual(Constants.TYPE_STRING_BUFFER, TO_STRING);
            e.return_value();
            e.end_method();

            ce.end_class();
        }
View Code
posted @ 2014-07-15 15:22  cruze_lee  阅读(3893)  评论(4编辑  收藏