代理6 cglib KeyFactory

作用:

Generates classes to handle multi-valued keys, for use in things such as Maps and Sets. Code for equals and hashCode methods follow the the rules laid out in Effective Java by Joshua Bloch.

什么叫multi-valued keys
就是有多个键的组合,一起作为一个Key
比如[a b c]是一个组合,一起作为key,[2 3]也可以是作为key

KeyFactory就是用来生成这样一组"Key"的
通过两组的equals,hashCode等方法判断是否为同一组key的场景

要求:

To generate a KeyFactory, you need to supply an interface which describes the structure of the key. The interface should have a single method named newInstance, which returns an Object. The arguments array can be anything--Objects, primitive values, or single or multi-dimension arrays of either. 

为了描述Key的组合,需要定义一个接口,仅提供一个方法,叫做newInstance,且返回值为Object,这个是要求

demo

参照自带的demo,位于cglib包下面samples.KeySample,自己略有改动

package samples;
import net.sf.cglib.core.DebuggingClassWriter;
import net.sf.cglib.core.KeyFactory;
public class KeySample {
    private interface MyFactory {
        public Object newInstance(int a, int b);
    }
    public static void main(String[] args) {
        //这一句是把class文件输出在指定目录
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, ".//");
        MyFactory f = (MyFactory)KeyFactory.create(MyFactory.class);
        MyFactory f1 = (MyFactory)KeyFactory.create(MyFactory.class);
        System.out.println(f == f1);//false
        System.out.println(f.equals(f1));//true
        Object key1 = f.newInstance(20, 30);
        Object key2 = f.newInstance(20, 30);
        Object key3 = f.newInstance(20, 40);

        System.out.println(key1.equals(key2));//true
        System.out.println(key2.equals(key3));//false
    }
}

可以看出

1.f和f1的关系,这实际上是用了缓存,缓存了同样的类对象,只不过生成不同的实例
2.key1和key2这个组合,是一样的,这是利用KeyFactory实现了equals和hash的方法
这样使得这种组合的Key能够用在map和set中

实现原理

demo中根据要求,拿到我们组合Key的对象是在

MyFactory f = (MyFactory)KeyFactory.create(MyFactory.class);

跟进去就是

net.sf.cglib.core.KeyFactory#create(java.lang.ClassLoader, java.lang.Class, net.sf.cglib.core.Customizer)
net.sf.cglib.core.KeyFactory.Generator#create
net.sf.cglib.core.AbstractClassGenerator#create

这里涉及到AbstractClassGenerator,这个是cglib中进行code-generating的工具类
看实现

protected Object create(Object key) {
        try {
            //需要缓存的类
            Class gen = null;
            synchronized (source) {
                ClassLoader loader = getClassLoader();
                Map cache2 = null;
                //根据来源区分,根据classLoader进行一级缓存
                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 (gen == null) {
                    Object save = CURRENT.get();
                    CURRENT.set(this);
                    try {
                        this.key = key;
                        if (attemptLoad) {
                            try {
                                gen = loader.loadClass(getClassName());
                            } catch (ClassNotFoundException e) {
                                // ignore
                            }
                        }
                        if (gen == null) {
                            //结合生成策略,得到类的字节码
                            byte[] b = strategy.generate(this);
                            String className = ClassNameReader.getClassName(new ClassReader(b));
                            getClassNameCache(loader).add(className);
                            //根据字节码生成类
                            gen = ReflectUtils.defineClass(className, b, loader);
                        }
                        if (useCache) {
                            //根据传入的key,进行二级缓存
                            cache2.put(key, new WeakReference(gen));
                        }
                        //根据类,生成实例
                        return firstInstance(gen);
                    } finally {
                        CURRENT.set(save);
                    }
                }
            }
            return firstInstance(gen);
        } catch (RuntimeException e) {
            throw e;
        } catch (Error e) {
            throw e;
        } catch (Exception e) {
            throw new CodeGenerationException(e);
        }
    }

说明:
1.缓存策略和之前讲动态代理的缓存比较
都用了二级缓存,一级为classLoader,二级为对应的interfaces
这里的source.cache是weakHashMap
这里的二级缓存的value是new WeakReference(gen),也是WeakReference

2.这里cglib的字节码生成

byte[] b = strategy.generate(this);

会用到默认实现,net.sf.cglib.core.DefaultGeneratorStrategy#generate
根据传入的ClassGenerator进行对应调用
这里实现在net.sf.cglib.core.KeyFactory.Generator#generateClass里面
主要是利用了asm,生成了asm文件一级class文件

public void generateClass(ClassVisitor v) {
            ClassEmitter ce = new ClassEmitter(v);
            
            Method newInstance = ReflectUtils.findNewInstance(keyInterface);
            if (!newInstance.getReturnType().equals(Object.class)) {
                throw new IllegalArgumentException("newInstance method must return Object");
            }

            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);
            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();

            // hash code
            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
            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();
        }

可以看出来利用asm完成了这个类的hashCode以及equals方法


代理6 cglib KeyFactory_第1张图片
image.png

可以看到,equals方法其实就是判断两个类对象(KeySample$MyFactory$$KeyFactoryByCGLIB$$7116a61e)的参数类型是否一致以及值是否相等

*** 3.equals方法这段代码是怎么generate出来的 ***
这一段是由asm中这段代码生成的

      // 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);
                //cast操作
                e.checkcast_this();
                e.getfield(getFieldName(i));
                //每个字段都equals
                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();

上面这一段可以大概理解asm里面是如何处理equals操作的

不具体展开asm的调用过程,只要知道整个class文件都是net.sf.cglib.core.KeyFactory.Generator#generateClass生成
然后在net.sf.cglib.core.DefaultGeneratorStrategy#generate转换成字节码的

思考

使用场景

需要多个键联合一起组成一个key的
参照samples.KeySample

这些Key组合的具体类型是什么,每次运行都不同

根据KeyFactory生成类文件,这里就是KeySample$MyFactory$$KeyFactoryByCGLIB$$7116a61e

生成类文件的过程

具体生成基本在net.sf.cglib.core.AbstractClassGenerator#create,依然用了二级缓存,reference

equals方法时如何generate的

asm

这里KeyFactory只是用来生成联合Key的,中间调用了AbstractClassGenerator生成类,但是这个类本身对于cglib有什么用?

在后面,根据一堆配置生成一个代理类的时候,结合缓存,就有用武之地了,配置中每一项组合成一个Key
在后面的demo以及源码解析会讲到

refer

http://cglib.sourceforge.net/apidocs/
http://www.cnblogs.com/cruze/p/3843996.html

你可能感兴趣的:(代理6 cglib KeyFactory)