cglib源码分析(一): 缓存和KEY
2014-07-15 15:22
239 查看
cglib是一个java 字节码的生成工具,它是对asm的进一步封装,提供了一系列class generator。研究cglib主要是因为它也提供了动态代理功能,这点和jdk的动态代理类似。
每个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()); 。
生成类的缓存是按照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 来标记是否使用缓存,这给了用户比较灵活的选择。
运行结果是:
为了生成multi-vaules 的工厂类,我们必须提供一个接口来描述multi-vaules的结构(上例中该接口为MyFactory),该接口有且只有一个方法newInstance,该方法的返回值必须为Object,该方法的入参可以是任意的对象,元数据类型 或者是任意维的数组 但是入参不能为空,如果为空就和默认的构造函数相冲突。 在分析KeyFactory之前,我们将上例生成的multi-vaules工厂类进行一下反编译(jd-gui 由于版本的问题无法反编译,因而此处使用javap):
View Code
一、 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 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
相关文章推荐
- ecshop源码分析——用cookie缓存sql语句
- ecshop源码分析——静态缓存
- MySpace DataRelay 分布式数据缓存源码分析[转]
- ecshop源码分析——静态缓存static_c…
- tcmalloc源码阅读(三)---ThreadCache分析之线程局部缓存
- 应用框架的设计与实现——.NET平台(5 缓存服务.源码分析)
- lucene4.5源码分析系列:索引缓存以及刷新
- jQuery源码分析-07数据缓存-Cache
- Guava缓存器源码分析——删除消息
- 看Volley源码,对HTTP缓存机制分析
- MySQL源码分析(5):Innodb缓存系统
- tcmalloc源码阅读(三)---ThreadCache分析之线程局部缓存
- jQuery1.9.1--data缓存对象源码分析
- MySQL源码:MYSQL存储过程/函数的分析原理及缓存机制
- Guava缓存器源码分析——删除消息
- ecshop源码分析——静态缓存
- MySQL源码:MYSQL存储过程/函数的分析原理及缓存机制
- MySpace DataRelay 分布式数据缓存源码分析[转]
- TCMalloc源码阅读(二)--线程局部缓存ClassSize分析
- cglib源码分析(四):cglib 动态代理原理分析