• 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
  • 相关阅读:
    C++调用WebService
    vs2005 环境的字体和颜色的设置
    自娱自乐~经典24点游戏
    再探利用C++的STL和堆栈编程思想实现数学四则运算计算结果
    如何在Linux中关闭apache服务(转)
    ios推送:本地通知UILocalNotification
    ASIHTTPRequest的使用(转)
    实战新浪微博、腾讯微博的分享功能(转)
    UIImagePickerController拍照与摄像(转)
    iOS通过http post上传图片 (转)
  • 原文地址:https://www.cnblogs.com/cruze/p/3843996.html
Copyright © 2020-2023  润新知