详解Java动态代理机制(二)----cglib实现动态代理

     上篇文章的结尾我们介绍了普通的jdk实现动态代理的主要不足在于:它只能代理实现了接口的类,如果一个类没有继承于任何的接口,那么就不能代理该类,原因是我们动态生成的所有代理类都必须继承Proxy这个类,正是因为Java的单继承,所以注定会抛弃原类型的父类。而我们的cglib通过扫描该类以及其父类中所有的public非final修饰的方法,通过asm定义该类的子类字节码,其中该子类重写了父类所有的方法,然后返回该子类的实例作为代理类。也就是说我们的cglib是用该类的子类作为代理类来实现代理操作的。当然cglib的缺点也是呼之欲出,对于被代理类中的非public或者final修饰的方法,不能实现代理。

     在详细介绍cglib之前,我们先简单介绍下ASM框架,这是一个小而快的字节码处理框架,它负责生成从被代理类中扫描出的方法的字节码并将这些方法生成字节码暂存在内存中。然后我们通过方法将这些字节码转换成class类型,最后利用反射创建代理类的实例返回。下面看一个完整的实例,稍后从源代码的角度分析这个实例:

//定义一个接口
public interface MyInterface {
    public void sayHello();
}
//定义一个ClassB类
public class ClassB {
    public void welcome(){
        System.out.println("welcom walker");
    }
}

//模拟被代理的类,继承了ClassB和接口MyInterface
public class ClassA extends ClassB implements MyInterface {
    public void sayHello(){
        System.out.println("hello walker");
    }
}
//定义一个回调实例,稍后解释
public class MyMethod implements MethodInterceptor {

    public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,
                            MethodProxy proxy) throws Throwable{
        proxy.invokeSuper(obj, args);
        return null;
    }
}
public class Test {
    public static void main(String[] args) throws Exception {

        ClassA ca = new ClassA();
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(ClassA.class);
        enhancer.setCallback(new MyMethod());
        ClassA my = (ClassA)enhancer.create();
        my.welcome();
    }
}
输出结果:welcom walker

我们看到,此处我们获取了ClassA的代理对象,然后调用了ClassA父类中的welcome方法。这也间接证明了我们通过cglib代理了ClassA的父类中的方法,这一点使用jdk实现的动态处理是做不到的。下面我们解释原理。

在这之前,由于cglib是第三方库,所以我们需要下载相应的jar文件,主要包含两个文件,一个是cglib的jar,还有一个是cglib依赖的ASM框架的jar文件,注意这两个jar的版本不能冲突。

我们从main方法的主体代码中可以看出,Enhancer 类是创建代理实例的核心类。没错,该类负责整个代理对象的生命周期,它就像是一个工具一样,提供了很多方法帮助我们创建代理实例。首先我们调用了setSuperclass方法设置父类型,其实也就是将被代理对象传入,因为我们之前说过cglib创建的代理类是原对象的子类型,所以这里称原类型实例为父类也是合理的。

跟进去,我们看到:

public void setSuperclass(Class superclass)
  {
    if ((superclass != null) && (superclass.isInterface())) {
      setInterfaces(new Class[] { superclass });
    } else if ((superclass != null) && (superclass.equals(Object.class))) {
      this.superclass = null;
    } else {
      this.superclass = superclass;
    }
  }

这段代码的主要意思是:如果传入的类型是接口的话,保存在专门用于保存接口类型的变量中。

private Class[] interfaces;

如果传入的类型是Object类型的话,将用于保存普通类类型的变量赋值为null,否则保存该传入的参数的值在该变量中。这些操作过程中保存的一些数值是为了在最后create的时候提供帮助。

接下来是setCallback方法,该方法设置了回调。也就是将来对我们代理中方法的访问会转发到该回调中,所有自定义的回调类必须继承MethodInterceptor接口并实现其intercept方法,这一点和jdk的InvocationHandler类似。这里的intercept有几个参数:

  • Object obj:被代理的原对象
  • Method method:被调用的当前方法
  • Object[] args:该方法的参数集合
  • MethodProxy proxy:被调用方法的代理,它可以和method完成同样的事情,但是它使用FastClass机制非反射执行方法,效率高

我们对于所有调用代理方法的请求,转发到invokeSuper方法中,该方法源码如下:

//fastclassinfo类
    private static class FastClassInfo
  {
    FastClass f1;
    FastClass f2;
    int i1;
    int i2;
    
    private FastClassInfo() {}
    
    FastClassInfo(MethodProxy.1 x0)
    {
      this();
    }
  }
  
 
  public Object invokeSuper(Object obj, Object[] args)
    throws Throwable
  {
    try
    {
      init();
      FastClassInfo fci = this.fastClassInfo;
      return fci.f2.invoke(fci.i2, obj, args);
    }
    catch (InvocationTargetException e)
    {
      throw e.getTargetException();
    }
  }

其中fastclassinfo类中,几个参数的意思解释下,f1指向被代理对象,f2指向代理类对象,i1和i2分别是代理类中的该方法的两个索引。也就是这种fastclass机制并不是通过反射找到指定的方法的,而是在创建代理类的时候为其中的方法建立hash索引,这样调用的时候通过索引调用提高了效率。最后调用了代理类的方法,也就是重写了父类的方法。

最后也是最核心的一步是create方法的调用,这个方法才是实际创建代理实例的方法,我们看源码:

  public Object create()
  {
    this.classOnly = false;
    this.argumentTypes = null;
    return createHelper();
  }

该方法主要设置了两个参数配置,指定将要创建的对象不仅仅是一个类,指定参数为空。至于这两个参数有何作用,还需要往下追,我们看createHelper类:

  private Object createHelper()
  {
    validate();
    if (this.superclass != null) {
      setNamePrefix(this.superclass.getName());
    } else if (this.interfaces != null) {
      setNamePrefix(this.interfaces[ReflectUtils.findPackageProtected(this.interfaces)].getName());
    }
    return super.create(KEY_FACTORY.newInstance(this.superclass != null ? this.superclass.getName() : null, ReflectUtils.getNames(this.interfaces), this.filter, this.callbackTypes, this.useFactory, this.interceptDuringConstruction, this.serialVersionUID));
  }

validate()方法主要对于一些参数进行校验,如果不符合创建实例的标准将抛出异常,我们可以简单的看一眼:

private void validate()
  {
    if ((this.classOnly ^ this.callbacks == null))
    {
      if (this.classOnly) {
        throw new IllegalStateException("createClass does not accept callbacks");
      }
      throw new IllegalStateException("Callbacks are required");
    }
    if ((this.classOnly) && (this.callbackTypes == null)) {
      throw new IllegalStateException("Callback types are required");
    }
    if ((this.callbacks != null) && (this.callbackTypes != null))
    {
      if (this.callbacks.length != this.callbackTypes.length) {
        throw new IllegalStateException("Lengths of callback and callback types array must be the same");
..........
..........
.........      
 }

主要还是判断回调是否指定,类型是否正确等,如果不符合创建条件就抛出异常。我们回去,接着就做了两个判断,用于指定被创建的代理类的名称,我们暂时不管他。又到了一个核心的方法,该方法将创建代理类并返回该类实例。首先我们看参数都是是什么意思,就一个参数,该参数是由KEY_FACTORY.newInstance方法返回的一个Object类型,我们看到在该方法的传入参数中,包括了父类类名或者接口名,回调类型,版本号等。该方法实际上返回了一个该代理类的一个唯一标识,这还不是关键,最关键的方法是这个create方法:

  protected Object create(Object key)
  {
    try
    {
      Class gen = null;
      synchronized (this.source)
      {
        ClassLoader loader = getClassLoader();
        Map cache2 = null;
        cache2 = (Map)this.source.cache.get(loader);
        if (cache2 == null)
        {
          cache2 = new HashMap();
          cache2.put(NAME_KEY, new HashSet());
          this.source.cache.put(loader, cache2);
        }
        else if (this.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 (this.attemptLoad) {
              try
              {
                gen = loader.loadClass(getClassName());
              }
              catch (ClassNotFoundException e) {}
            }
            if (gen == null)
            {
              b = this.strategy.generate(this);
              String className = ClassNameReader.getClassName(new ClassReader(b));
              getClassNameCache(loader).add(className);
              gen = ReflectUtils.defineClass(className, b, loader);
            }
            if (this.useCache) {
              cache2.put(key, new WeakReference(gen));
            }
            byte[] b = firstInstance(gen);
            
            CURRENT.set(save);return b;
          }
          finally
          {
            CURRENT.set(save);
          }
        }
      }
      return firstInstance(gen);
//省去了异常捕获的代码块

如果usecache为为true表明该代理类已经在cache中了,直接返回引用即可。否则通过 this.strategy.generate(this);方法生成该代理类的字节码,然后通过通过类加载器加载该字节码生成class类型,最后通过firstInstance方法生成代理类的实例返回。

最后我们看一眼刚才生成的代理的源码:

//代码很多,此处贴出部分
public class ClassA$$EnhancerByCGLIB$$64984e8e extends ClassA
    implements Factory
{
    final void CGLIB$sayHello$0()
    {
            super.sayHello();
    }
    public final void sayHello()
    {
        CGLIB$CALLBACK_0;
        if(CGLIB$CALLBACK_0 != null) goto _L2; else goto _L1
_L1:
        JVM INSTR pop ;
        CGLIB$BIND_CALLBACKS(this);
        CGLIB$CALLBACK_0;
_L2:
        JVM INSTR dup ;
        JVM INSTR ifnull 37;
           goto _L3 _L4
_L3:
        break MISSING_BLOCK_LABEL_21;
_L4:
        break MISSING_BLOCK_LABEL_37;
        this;
        CGLIB$sayHello$0$Method;
        CGLIB$emptyArgs;
        CGLIB$sayHello$0$Proxy;
        intercept();
        return;
        super.sayHello();
        return;
    }

    final void CGLIB$welcome$1()
    {
        super.welcome();
    }

    public final void welcome()
    {
        CGLIB$CALLBACK_0;
        if(CGLIB$CALLBACK_0 != null) goto _L2; else goto _L1
_L1:
        JVM INSTR pop ;
        CGLIB$BIND_CALLBACKS(this);
        CGLIB$CALLBACK_0;
_L2:
        JVM INSTR dup ;
        JVM INSTR ifnull 37;
           goto _L3 _L4
_L3:
        break MISSING_BLOCK_LABEL_21;
_L4:
        break MISSING_BLOCK_LABEL_37;
        this;
        CGLIB$welcome$1$Method;
        CGLIB$emptyArgs;
        CGLIB$welcome$1$Proxy;
        intercept();
        return;
        super.welcome();
        return;
    }
....
....
}

从中我们看到,该类ClassA$$EnhancerByCGLIB$$64984e8e继承自ClassA,实现了接口factory。并且在其中我们看到不仅是父类ClassA中的方法sayHello在其中被重写了之外,ClassA的父类ClassB中的welcome方法也被重写了。足以见得,cglib利用继承的方式动态创建了被代理类的子类,通过ASM生成父类中所有public非final修饰的方法,实现了代理。

最后稍微小结下,cglib的实现代理的逻辑。首先我们通过Enhancer实例设置被代理类,然后设置该代理类的回调,也就是在访问代理类方法的时候会首先转向该回调,在回调中我们调用invokeSuper方法以fastclass这种非反射机制快速的调用到代理类中的方法,其中代理类中方法又调用原类型的对应方法。

由于cglib已经停止维护好多年,导致参考文档很少,学习难度很大,此篇文章也是作者研读jdk和网上优秀博文总结,不当之处,望大家指出,学习 !学习!

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏WindCoder

学生信息排序、查找及修改

491
来自专栏海说

Tomcat Server处理一个http请求过程

假设来自客户端的请求为:          http://localhost:8080/lizhx/lizhx_index.jsp 请求被发送到本机端口8080...

1850
来自专栏林德熙的博客

wpf 绑定 TextLength

我看到朋友的代码出现绑定了 一个 TextBlock 的 Text 的 length ,那时候我觉得 length 不是依赖属性,绑定了是无法通知的。最后我做了...

531
来自专栏程序你好

.NET面试基础知识

不同公司的职位和工作职责不同。在面试中,工作职责和经验对这个职位很重要。程序员职位有一年的经验他们会关注oops概念、并行编程、算法和解决问题的能力等等。另一方...

692
来自专栏余林丰

Spring AOP高级——源码实现(1)动态代理技术

jdk1.8.0_144   在正式进入Spring AOP的源码实现前,我们需要准备一定的基础也就是面向切面编程的核心——动态代理。 动态代理实际上也是一种...

21510
来自专栏Danny的专栏

静态代理与动态代理

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/huyuyang6688/article/...

453
来自专栏salesforce零基础学习

salesforce 零基础学习(二十二)Test简单使用

本篇内容只是本人简单的mark开发中常出现的一些疑问,方便后期项目使用时奠定基础,如果对Test零基础童鞋,欢迎查看Test官方的使用介绍:  https://...

18210
来自专栏Netkiller

Java 反射,开发框架必备技能

反射一般开发者接触不到,反射主要用户框架的开发。例如我举一个例子你就明白了: http://www.netkiller.cn/news/list/2.html...

2675
来自专栏Danny的专栏

探秘static——类不需实例化就能用?

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/huyuyang6688/article/...

684
来自专栏Lambda

Java 常见内存溢出异常与代码实现

Java 堆 OutOfMemoryError Java 堆是用来存储对象实例的, 因此如果我们不断地创建对象, 并且保证 GC Root 和创建的对象之间...

1848

扫码关注云+社区