cglib源码分析1 ----- 缓存和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

}

从反编译的结果我们可以看出,生成的工厂类为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();
        }

cglib源码分析1 ----- 缓存和KEY

时间: 2024-10-10 16:39:41

cglib源码分析1 ----- 缓存和KEY的相关文章

cglib源码分析2 ----- Class name 生成策略

一.如何获取动态生成的class 字节码 结合生成的class文件是一个学习cglib的比较好的方法.在cglib中,生成的class文件默认只存储在内存中,我们可以在代码中加入下面语句来获取class file. System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "F:\\code "); 这里涉及到了一个比较关键的类DebuggingClassWriter,接下来我们简单的分析一下这个类.Debuggi

jQuery1.9.1源码分析--数据缓存Data模块

阅读目录 jQuery API中Data的基本使用方法介绍 jQuery.acceptData(elem)源码分析 jQuery.data(elem, name, data)源码分析 internalRemoveData方法源码分析 internalData方法的源码分析 jQuery.fn.extend({data: function( key, value ) {}})源码分析 jQuery.extend({removeData: function( elem, name ) {}})源码分

lodash源码分析之缓存使用方式的进一步封装

在世界上所有的民族之中,支配着他们的喜怒选择的并不是天性,而是他们的观点. --卢梭<社会与契约论> 本文为读 lodash 源码的第九篇,后续文章会更新到这个仓库中,欢迎 star:pocket-lodash gitbook也会同步仓库的更新,gitbook地址:pocket-lodash 前言 在之前的<lodash源码分析之Hash缓存>和<lodash源码分析之List缓存>介绍过 lodash 的两种缓存方式,在<lodash源码分析之缓存方式的选择&g

【转】MaBatis学习---源码分析MyBatis缓存原理

[原文]https://www.toutiao.com/i6594029178964673027/ 源码分析MyBatis缓存原理 1.简介 在 Web 应用中,缓存是必不可少的组件.通常我们都会用 Redis 或 memcached 等缓存中间件,拦截大量奔向数据库的请求,减轻数据库压力.作为一个重要的组件,MyBatis 自然也在内部提供了相应的支持.通过在框架层面增加缓存功能,可减轻数据库的压力,同时又可以提升查询速度,可谓一举两得.MyBatis 缓存结构由一级缓存和二级缓存构成,这两级

cglib源码分析(三):Class生成策略

cglib中生成类的工作是由AbstractClassGenerator的create方法使用相应的生成策略完成,具体代码如下: private GeneratorStrategy strategy = DefaultGeneratorStrategy.INSTANCE; byte[] b = strategy.generate(this); GeneratorStrategy是一个接口,它负责调用ClassGenerator 的generateClass方法来生成类.DefaultGenera

cglib源码分析--转

原文地址:http://www.iteye.com/topic/799827 背景 前段时间在工作中,包括一些代码阅读过程中,spring aop经常性的会看到cglib中的相关内容,包括BeanCopier,BulkBean,Enancher等内容,以前虽大致知道一些内容,原理是通过bytecode,但没具体深入代码研究,只知其所用不知其所以然,所以就特地花了半天多的工作时间研究了CGLIB的相关源码,同时结合看了下 spring Aop中对CGLIB的使用. 本文主要通过对cglib有原理的

jQuery 2.0.3 源码分析 数据缓存

转载http://www.cnblogs.com/aaronjs/p/3370176.html 历史背景: jQuery从1.2.3版本引入数据缓存系统,主要的原因就是早期的事件系统 Dean Edwards 的 ddEvent.js代码 带来的问题: 没有一个系统的缓存机制,它把事件的回调都放到EventTarget之上,这会引发循环引用 如果EventTarget是window对象,又会引发全局污染 不同模块之间用不同缓存变量 一般jQuery开发,我们都喜欢便捷式的把很多属性,比如状态标志

Mybatis源码分析自定义缓存、分页的实现

一.缓存 我们知道,在Mybatis中是有缓存实现的.分一级缓存和二级缓存,不过一级缓存其实没啥用.因为我们知道它是基于sqlSession的,而sqlSession在每一次的方法执行时都会被新创建.二级缓存是基于namespace,离开了它也是不行.有没有一种方式来提供自定义的缓存机制呢? 1.Executor Executor是Mybatis中的执行器.所有的查询就是调用它的<E> List<E> query()方法.我们就可以在这里进行拦截,不让它执行后面的查询动作, 直接从

MyBatis源码分析(五):MyBatis Cache分析

一.Mybatis缓存介绍 在Mybatis中,它提供了一级缓存和二级缓存,默认的情况下只开启一级缓存,所以默认情况下是开启了缓存的,除非明确指定不开缓存功能.使用缓存的目的就是把数据保存在内存中,是应用能更快获取数据,避免与数据库频繁交互,特别是在查询比较多.命中率比较高的情况下,缓存就显得很重要.但是使用不得当,会产生脏数据. 二.目录 一级缓存介绍及相关配置. 一级缓存工作流程及源码分析. 一级缓存总结. 二级缓存介绍及相关配置. 二级缓存源码分析. 二级缓存总结. 全文总结. 三.一级缓