cglib创建代理对象(2)

首先得从类图开始,

在这里插入图片描述

总体流程说明:

通常的使用是直接通过Enhancer来创建代理对象,Enhancer继承于AbstractClassGenerator,AbstractClassGenerator实现了ClassGenerator,ClassGenerator接口的作用是能够通过一个ClassVisitor来生成byte[]。

在AbstractClassGenerator里面并没有直接实现了ClassGenerator#generateClass方法,而是给具体的实现类去实现,在不同的实现类里面能利用ClassVisitor来生成特定的类。比如说下图:

在这里插入图片描述

我们的重点在于AbstractClassGenerator。AbstractClassGenerator类里面增加了对类的缓存,提供了自定义的hook方法可以对代理类的名字修改,此外,还可以在应用之前做转化。

AbstractClassGenerator中主要的方法是create方法。提供了一个key是ClassLoader, v是ClassLoaderData的缓存,默认是使用缓存的。可以通过set方法关闭掉,也可以通过修改环境变量cglib.useCache为false来关闭掉。如果不走缓存,直接调用AbstractClassGenerator#generate(ClassLoaderData)方法

走缓存,要是缓存没有的话,最终还是会走到AbstractClassGenerator#generate(ClassLoaderData)方法。只不过,多了一个缓存的操作。等会详细的说这个缓存。

AbstractClassGenerator#generate(ClassLoaderData)里面。首先要确定类的名字,并且类名字不能重复。还可以通过attemptLoad来决定是否要先对这个类名先加载一下,如果加载成功了,就直接返回,attemptLoad值是可以通过set方法来修改得。

这个时候类得名字已经确定了,就可以通过GeneratorStrategy来生成一个byte[]了,这个时候已经在内存里面生成class文件了,通过ClassVisitor对生成得byte[]读取,直接读取class文件中得类名字,通过Class.forName获取class对象。

获取到Class对象之后,还可以通过AbstractClassGenerator#unwrapCachedValue来做增强。

到这里,代理类已经搞好了,就剩最后一步,创建实例。在这里要做一个判断,因为它整个都是范型,这里它增加了判断,为什么要增加这个判断,因为在generate方法里面很明确已经要返回class对象,将生成得class对象传递给unwrapCachedValue,他得返回值是Object。这里面就保不齐出什么幺蛾子了。所以增加了判断。对于Class类型得直接调用firstInstance(Class ),否则就是nextInstance(Object)这俩方法都是留给子类来实现得。

带着上面的大体的流程概念,开始下面分析。

第一个问题是看看缓存,ClassLoaderData长什么样子

ClassLoaderData

这个类没有啥可说,提供了两个Function,GET_KEY(key的生成策略),load(value的加载方式),缓存真正的实现是在LoadingCache里面,下面就看看他是怎么写的。

 protected static class ClassLoaderData {
     // 保存的是代理类的名字,
        private final Set<String> reservedClassNames = new HashSet<String>();
      // 缓存
        private final LoadingCache<AbstractClassGenerator, Object, Object> generatedClasses;
        private final WeakReference<ClassLoader> classLoader;
       // 利用他来做代理类的唯一性
        private final Predicate uniqueNamePredicate = new Predicate() {
            public boolean evaluate(Object name) {
                return reservedClassNames.contains(name);
            }
        };
      // key的生成
        private static final Function<AbstractClassGenerator, Object> GET_KEY = new Function<AbstractClassGenerator, Object>() {
            public Object apply(AbstractClassGenerator gen) {
                return gen.key;
            }
        };
     // 构造方法,主要是创建了LoadingCache
        public ClassLoaderData(ClassLoader classLoader) {
            if (classLoader == null) {
                throw new IllegalArgumentException("classLoader == null is not yet supported");
            }
            this.classLoader = new WeakReference<ClassLoader>(classLoader);  //
            Function<AbstractClassGenerator, Object> load =  // 最后得get方法
                    new Function<AbstractClassGenerator, Object>() {
                        public Object apply(AbstractClassGenerator gen) {
                            Class klass = gen.generate(ClassLoaderData.this);
                            return gen.wrapCachedClass(klass);
                        }
                    };
            generatedClasses = new LoadingCache<AbstractClassGenerator, Object, Object>(GET_KEY, load); // 创建loadCache
        }

        public ClassLoader getClassLoader() {
            return classLoader.get();
        }

        public void reserveName(String name) {
            reservedClassNames.add(name);
        }

        public Predicate getUniqueNamePredicate() {
            return uniqueNamePredicate;
        }
     // 先从缓存中找,如果不需要走缓存,直接调用AbstractClassGenerator#generate方法,否则就要走缓存
        public Object get(AbstractClassGenerator gen, boolean useCache) {
            if (!useCache) {
              return gen.generate(ClassLoaderData.this);
            } else {
              Object cachedValue = generatedClasses.get(gen);
                  return gen.unwrapCachedValue(cachedValue);
            }
        }
    }

LoadingCache分析

在获取的时候,会通过传递进来的key(这里叫做主key),通过keyMapper来产生一个子key,子key用作缓存的key,通过主key去加载value。需要注意的是,value的加载是通过FutureTask来加载的,生成Class时候的线程安全

在ClassLoaderData使用它的时候,会将ClassLoaderData#GET_KEY和ClassLoaderData#load传递过来。所以LoadingCache本身就是一个外壳。内容是通过Function来加载的。这样我想到了缓存的实现。

缓存的实现,应该有下面的几个特点

  1. 缓存的吞吐率
  2. 缓存的命中率
  3. 其他
    1. 缓存大小
    2. 缓存的淘汰策略
    3. 缓存淘汰方式
    4. key和value的加载。
    5. 缓存的过期时间

其实我感觉,这个类在日常也是可以用的。

发现了一个问题,既然是hashMap,key是要用作hash计算的,问题来了,在Cglib中它是在那里引用的?

从上面可以知道,就是下图中的代码

在这里插入图片描述

是AbstractClassGenerator#key的key,那么它的hashCode是怎么保证的,它是在那里调用和赋值的。继续往下面看。

public class LoadingCache<K, KK, V> {
    protected final ConcurrentMap<KK, Object> map; // 缓存的map
    protected final Function<K, V> loader; // value的加载
    protected final Function<K, KK> keyMapper;//keyMapper的生成
    public static final Function IDENTITY = new Function() {
        public Object apply(Object key) {
            return key;
        }
    };
    public LoadingCache(Function<K, KK> keyMapper, Function<K, V> loader) {
        this.keyMapper = keyMapper;
        this.loader = loader;
        this.map = new ConcurrentHashMap<KK, Object>();
    }

    @SuppressWarnings("unchecked")
    public static <K> Function<K, K> identity() {
        return IDENTITY;
    }
   
    public V get(K key) {
        final KK cacheKey = keyMapper.apply(key);
        Object v = map.get(cacheKey);
        if (v != null && !(v instanceof FutureTask)) {
            return (V) v;
        }
        //
        return createEntry(key, cacheKey, v);
    }


    protected V createEntry(final K key, KK cacheKey, Object v) {
        FutureTask<V> task;
        boolean creator = false;
        if (v != null) {
            // Another thread is already loading an instance
            task = (FutureTask<V>) v;
        } else {
            task = new FutureTask<V>(new Callable<V>() {
                public V call() throws Exception {
                    return loader.apply(key);
                }
            });
            Object prevTask = map.putIfAbsent(cacheKey, task);
            if (prevTask == null) {
                // creator does the load
                creator = true;
                task.run();
            } else if (prevTask instanceof FutureTask) {
                task = (FutureTask<V>) prevTask;
            } else {
                return (V) prevTask;
            }
        }

        V result;
        try {
            result = task.get();
        } catch (InterruptedException e) {
            throw new IllegalStateException("Interrupted while loading cache item", e);
        } catch (ExecutionException e) {
            Throwable cause = e.getCause();
            if (cause instanceof RuntimeException) {
                throw ((RuntimeException) cause);
            }
            throw new IllegalStateException("Unable to load cache item", cause);
        }
        // 这种写法很常见,出了异常就不会放里面了
        if (creator) {
            map.put(cacheKey, result);
        }
        return result;
    }
}

NamingPolicy(代理类名字的生成策略)

用来生成代理类的名字,方法有四个参数,prefix,source,key,names(在创建代理类名字的时候利用它来做唯一性,就是利用上面说的ClassLoaderData#uniqueNamePredicate用来判断)。

看看source,如果source中有.,就会取最后一个.的值,如果没有.就是全部,因为如果没有,lastIndexOf为-1,-1+1 = 0,就是全部。

剩下的三个的意思看它这里的代码逻辑就好了,我觉得重要的是看调用它的地方所赋予它的语义。还是老办法,点看调用看看。在这里插入图片描述

都是AbstractClassGenerator中的属性,都提供了set方法来设置,namePerfix是String,没有啥可说的,但是source和key是什么?

在这里插入图片描述

source就是一个语义,就是用来生成名字的,key是一个Object。代表它是一个可以随便放的。那它的hashcode是怎么保证的,继续往下看。

为了解释清楚它的hashcode是怎么保证的。在此之前,我们回头看看AbstractClassGenerator#generate方法里面调用了GeneratorStrategy#generate方法。

public class DefaultNamingPolicy implements NamingPolicy {
    public static final DefaultNamingPolicy INSTANCE = new DefaultNamingPolicy();

   
    private final static boolean STRESS_HASH_CODE = Boolean.getBoolean("net.sf.cglib.test.stressHashCodes");
    
    public String getClassName(String prefix, String source, Object key, Predicate names) {
        if (prefix == null) {
            prefix = "net.sf.cglib.empty.Object";
        } else if (prefix.startsWith("java")) {
            prefix = "$" + prefix;
        }
        String base =
            prefix + "$$" + 
            source.substring(source.lastIndexOf('.') + 1) +
            getTag() + "$$" +
            Integer.toHexString(STRESS_HASH_CODE ? 0 : key.hashCode());
        String attempt = base;
        int index = 2;
        while (names.evaluate(attempt))
            attempt = base + "_" + index++;
        return attempt;
    }

    protected String getTag() {
        return "ByCGLIB";
    }

  public int hashCode() {
    return getTag().hashCode();
  }

  public boolean equals(Object o) {
    return (o instanceof DefaultNamingPolicy) && ((DefaultNamingPolicy) o).getTag().equals(getTag());
  }
}

在这个方法里面会调用key的hashcode值,所以,我们要看看key是在那里赋值的?还是老办法,点看看看调用

在这里插入图片描述

发现,在调用create方法的时候传递进来了key,随便点击一个方法,会发现下面的代码

KEY_FACTORY.newInstance(.......)的方法

直接说答案吧,是通过KeyFactory来保证key的hashcode的值的。这也是动态生成的,在下面的章节会单独拿出来说。

到这里,理清了,Source和Key得作用。

Source是用作代理类名字生成得,他是作为中间值填充得。

Key是用作缓存得,同时还用于代理类名字得生成,他是作为后面得值填充得。(后面填充使用得是他得hashcode)。

GeneratorStrategy

DefaultGeneratorStrategy是GeneratorStrategy默认实现类,直接看它。

创建DebuggingClassWriter,DebuggingClassWriter继承于ClassVisitor,ClassVisitor是asm的类。

最终会调用到ClassGenerator#generateClass,并将DebuggingClassWriter传递过来,留给子类通过DebuggingClassWriter来自定义代理类。

对了在toByteArray方法有之前说的cglib.debugLocation,会将生成的class文件保存在文件系统中。

当然,也可以重写transform()方法来做ClassGenerator得转换或者生产得字节码得改变。这里是可以改变得,因为现在得byte[] 还没有加载。

public class DefaultGeneratorStrategy implements GeneratorStrategy {
    public static final DefaultGeneratorStrategy INSTANCE = new DefaultGeneratorStrategy();
    
    public byte[] generate(ClassGenerator cg) throws Exception {
        DebuggingClassWriter cw = getClassVisitor();
        transform(cg).generateClass(cw);
        return transform(cw.toByteArray());
    }
   // DebuggingClassWriter 是继承于 ClassVisitor,lcnote 让我看看这个是干嘛得
    // 回答 这个类是做 “主要负责 “拜访” 类成员信息。其中包括(标记在类上的注解,类的构造方法,类的字段,类的方法,静态代码块”
    // [深入字节码 -- ASM 关键接口 ClassVisitor](https://blog.csdn.net/ywb201314/article/details/53117183)
    protected DebuggingClassWriter getClassVisitor() throws Exception {
      return new DebuggingClassWriter(ClassWriter.COMPUTE_FRAMES);
    }

    protected final ClassWriter getClassWriter() {
	// Cause compile / runtime errors for people who implemented the old
	// interface without using @Override
	throw new UnsupportedOperationException("You are calling " +
		"getClassWriter, which no longer exists in this cglib version.");
    }
    
    protected byte[] transform(byte[] b) throws Exception {
        return b;
    }

    protected ClassGenerator transform(ClassGenerator cg) throws Exception {
        return cg;
    }
}

来到这里,就看看ClassGenerator#generateClass干了什么事情

ClassGenerator#generateClass

它有很多得实现类,每一个实现类里面都有自己自定义得操作,

在这里插入图片描述

这里就不挨个分析了,加上我对字节码得操作着实是不懂,就不详细得解释了。简单得说说Enhancer得流程吧。

  1. 一开始得方法还是一波判断,比如说,常量修饰得类,是不能够做代理得。

  2. 确定被代理得类中得那些方法是不能代理得(对应代码是Enhancer#getMethods

    • 拿到所有得方法。这里面就有的说了,他会拿到代理类的所有方法(包括父类,实现接口,接口的父接口),拿到方法之后,会有一个去重的操作

      去重是通过HashSet来完成的,有一个小tips,主要是利用到了Hash,就要看它的hashCode方法,放在HashSet中的key的HashCode是怎么产生的。

      利用 KeyFactory来做的,对应的是(Enhancer#getMethods中调用的MethodWrapper#create方法)。在它里面是通过KeyFactory来保证Hashcode的。关于KeyFactory,下面的章节会说,对了,突然想起来,还记得上面说的那个key吗?key的hash值也是通过**KeyFactory**来做的。

    • 增加过滤器来判断。(对应代码就是Predicate得实现类,和CollectionUtils#filter(Collection,Predicate)方法),至于具体的方法能不能重写,(Public,Private,Protected)方法能不能重写,得看具体的实现类里面的判断逻辑了,但是Private方法不能重写

在这里插入图片描述

  1. 当然,构造方法也得来一下,除了Predicate之后(Enhancer#filterConstructors),多了一个transform操作,具体的代码在Enhancer#generateClass里面调用的 CollectionUtils.transform(Collection,Transformer)方法

  2. 方法已经确定好了,下面开始得写字段了,这些字段并不是全部要用到的字段,只是一些固定的字段,具体变化的字段在方法写入的时候操作的。这些字段都是在代理类里面用的到的。
    在这里插入图片描述

  3. 重点来了,下面要开始写方法了,因为Callback有很多的类型,CallbackFilter用来确定到底使用哪个Callback,这里肯定有这样的操作,对应的操作就是遍历,将method传递给filter,filter返回下标来确定到底要用哪个Callback。这一块对应的代码在(Enhancer#emitMethods

    此外,还提供了CallbackGenerator来做不同类型Callback的写入操作,将被代理的对象的方法按照类型分组,调用不同的CallbackGenerator来写入。

在这里插入图片描述

​ (callback类型和CallbackGenerator的映射关系)

在这里插入图片描述

不同的CallbackGenerator有不同的处理逻辑,就拿Protected修饰的方法来说,在LazyLoaderGenerator,DispatcherGenerator Protected修饰的方法是不能重写的,但是在别的里面就是可以的。

具体写方法的流程我就不分析了,因为我也不太清楚。

  1. 写完方法之后,还得写构造方法。

  2. 所有由Enhancer生成的代理类,是必须要实现Factory接口的,那么最后的操作就是实现Factory接口,并且生成代码。

到这里,代理类class文件已经生成了。剩下的几个步骤就是加载class文件,实例化了。下面说说**KeyFactory**

KeyFactory分析

通过KeyFactory可以处理多个key,通过KeyFactory生成的对象,可以很方便的处理hashCode,和equals方法。并且生成的对象是遵从《 Effective Java 》中关于hashcode和equals的规定的。

此外,因为通过它来生成对象,这个对象得实现接口,否则KeyFactory它都不知道你有什么方法。所以,它要求接口必须的名字必须是newInstance,返回值必须是Object

简单的说,这个类可方便的生成hashcode,和equals。参数不限制,只要方法名和返回值符合要求就行。

正因为它有这样的特性,所以,作为缓存的key,才能保证它的唯一性。也可以利用它来做Hashset的去重操作。

列子

  1. 接口

    自定义的接口,需要传递三个参数作为hashcode和equals方法判断的因素。

    package net.sf.cglib.samples.keyfactory;
    public interface TestKey {
        Object newInstance(String name,ClassLoader classLoader,String className);
    }
    
  2. 测试类

    public class MainTest {
        public static void main(String[] args) {
                System.getProperties().setProperty("cglib.debugLocation", "C:\\Users\\Administrator\\Desktop\\temp");
            TestKey keyFactory = (TestKey)KeyFactory.create(TestKey.class);
            Object a = keyFactory.newInstance("a", Thread.currentThread().getContextClassLoader(),
                    TestKey.class.getName());
            Object b = keyFactory.newInstance("a", Thread.currentThread().getContextClassLoader(),
                    TestKey.class.getName());
            System.out.println("a:hashCode" + a.hashCode());
            System.out.println("b:hashCode" + b.hashCode());
    
            System.out.println(a.equals(b));
    
            System.out.println(a.getClass());
    
        }
    }
    
  3. 生成的代理类

    还是上面的老办法,将生成的class文件直接拖到idea里面去。

    首先看属性,newInstance方法的入参都作为一个个属性,属性的名字是FIELD_开头,后面累加。

    从下面的代码可以看到,newInstance其实很简单,每次都是创建一个新的代理类。在它里面,每次都是赋值,new的操作。

    重点是看的hashcode和equals方法。newInstance的属性,在这俩方法里面都用到了。

    //
    // Source code recreated from a .class file by IntelliJ IDEA
    // (powered by FernFlower decompiler)
    //
    
    package net.sf.cglib.samples.keyfactory;
    
    import net.sf.cglib.core.KeyFactory;
    
    public class TestKey$$KeyFactoryByCGLIB$$c8c30c06 extends KeyFactory implements TestKey {
        private final String FIELD_0;
        private final ClassLoader FIELD_1;
        private final String FIELD_2;
    
        public TestKey$$KeyFactoryByCGLIB$$c8c30c06() {
        }
    
        public Object newInstance(String var1, ClassLoader var2, String var3) {
            return new TestKey$$KeyFactoryByCGLIB$$c8c30c06(var1, var2, var3);
        }
    
        public TestKey$$KeyFactoryByCGLIB$$c8c30c06(String var1, ClassLoader var2, String var3) {
            this.FIELD_0 = var1;
            this.FIELD_1 = var2;
            this.FIELD_2 = var3;
        }
    
        public int hashCode() {
            int var10002 = 1209107 * 74391461;
            String var10001 = this.FIELD_0;
            var10002 = (var10002 + (var10001 != null ? var10001.hashCode() : 0)) * 74391461;
            ClassLoader var1 = this.FIELD_1;
            var10002 = (var10002 + (var1 != null ? var1.hashCode() : 0)) * 74391461;
            var10001 = this.FIELD_2;
            return var10002 + (var10001 != null ? var10001.hashCode() : 0);
        }
    
        public boolean equals(Object var1) {
            if (var1 instanceof TestKey$$KeyFactoryByCGLIB$$c8c30c06) {
                String var10000 = this.FIELD_0;
                String var10001 = ((TestKey$$KeyFactoryByCGLIB$$c8c30c06)var1).FIELD_0;
                if (var10001 == null) {
                    if (var10000 != null) {
                        return false;
                    }
                } else if (var10000 == null || !var10000.equals(var10001)) {
                    return false;
                }
    
                ClassLoader var2 = this.FIELD_1;
                ClassLoader var3 = ((TestKey$$KeyFactoryByCGLIB$$c8c30c06)var1).FIELD_1;
                if (var3 == null) {
                    if (var2 != null) {
                        return false;
                    }
                } else if (var2 == null || !var2.equals(var3)) {
                    return false;
                }
    
                var10000 = this.FIELD_2;
                var10001 = ((TestKey$$KeyFactoryByCGLIB$$c8c30c06)var1).FIELD_2;
                if (var10001 == null) {
                    if (var10000 == null) {
                        return true;
                    }
                } else if (var10000 != null && var10000.equals(var10001)) {
                    return true;
                }
            }
    
            return false;
        }
    
        public String toString() {
            StringBuffer var10000 = new StringBuffer();
            String var10001 = this.FIELD_0;
            var10000 = (var10001 != null ? var10000.append(var10001.toString()) : var10000.append("null")).append(", ");
            ClassLoader var1 = this.FIELD_1;
            var10000 = (var1 != null ? var10000.append(var1.toString()) : var10000.append("null")).append(", ");
            var10001 = this.FIELD_2;
            return (var10001 != null ? var10000.append(var10001.toString()) : var10000.append("null")).toString();
        }
    }
    

分析

直接从KeyFactory#create方法入手,在KeyFactory里面,有一个内部类Generator,它继承了AbstractClassGenerator接口,通过上面的分析,这里就直接看Generator#generateClass操作了。在这前面在看看几个常规的问题。

  1. Source是什么?

在这里插入图片描述

想想,Source的作用,所以,上面生成的代理类名字里面是有KeyFactory的。

  1. 既然继承了AbstractClassGenerator,那么在Generator里面,传递的缓存的key是啥?

在这里插入图片描述

传递的key是要实现的接口的名字。所以,同样的接口,再次通过KeyFactory来创建,就会命中缓存。

  1. KeyFactoryCustomizer的作用是什么?

    KeyFactoryCustomizer是KeyFactory#create方法的参数,它的作用是在构建equals,hashcode,toString方法时候的回调。通过它就可以自定义操作了。

开始看Generator#generateClass方法

这里我还是大体的说说流程,具体的就不说了,因为我也不太明白。具体的代码逻辑在KeyFactory#Generator#generateClass里面。

  1. 首先第一件事情还是校验(方法名字的校验,方法返回值的校验)

  2. 这没有Enhancer那么复杂的操作。就那几个方法。先写一个无参构造。

  3. 在实现接口的方法,

  4. 声明字段(KeyFactory#getFieldName)

  5. 写方法(在这里会用到KeyFactoryCustomizer)

    需要注意KeyFactoryCustomizer在之后用的时候有特殊的处理,会将所有的KeyFactoryCustomizer添加到CustomizerRegistry中,组成map,map的key是要处理的class,map的v是List<KeyFactoryCustomizer>,具体的可以去看看CustomizerRegistry创建和添加的相关代码.

到这里,代理对象创建的一个大体的流程就分析完了。结束。还有一个问题,Cglib中MethodInterceptor拦截器来做方法调用的时候,会有什么操作,可记得在上一篇中,在生成的代理类中可是有MethodProxy的操作的,剩下的几个Callback,没有什么特殊的地方。通过MethodProxy来调用父类方法会发生什么?cglib是可以检测到被代理对象方法之间调用的吗?下一篇分析。

到此,结束了。

关于博客这件事,我是把它当做我的笔记,里面有很多的内容反映了我思考的过程,因为思维有限,不免有些内容有出入,如果有问题,欢迎指出。一同探讨。谢谢。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值