当前位置:网站首页 > 技术博客 > 正文

cglib底层实现原理



cglib 是一个功能强大、高性能、高质量的字节码操作库,主要用于在运行时扩展 Java 类或者根据接口生成对象。

cglib 在一些开源框架中使用很广泛,比如 Spring, 数据库开源库 Hibernate,以及测试框架 mockito。正是因为 cglib 把脏活累活都干了,这些框架使用才很方便。

这是一个开源的库,cglib 本身的实现基于 asm 库。相比于 asm 库,cglib 的接口更友好,使用起来更简单。

下面介绍 cglib 的主要接口和类以及基于 cglib 的动态代理实现。

💡 本文基于 OpenJDK11

首先要说到的就是 Enhancer 这个类,这个是 cglib 中使用的最多的类。之前 JDK 中使用反射来实现动态代理时,必须要基于接口来生成动态代理类,而 Enhancer 可以直接基于类来代理类。

Enhancer 可以生成被代理类的子类,并且拦截所有方法的调用,也就是通常所说的增强

需要注意,Enhancer 不能增强构造函数,也不能增强被 final 修饰的,或者被 static 和 final 修饰的方法

如果不想直接生成一个对象,cglib 也可以生成一个 Class 对象,用这个 Class 对象生成对象或者其他操作。

Enhancer 的使用分为两步,传入目标类型,设置回调。支持不同类型回调是 cglib 最强大的地方。

 
  

在 Enhancer 创建一个代理类之后所实现的行为要通过这些回调来实现。

常见的的回调类型如下:

  • FixedValue:返回一个固定的值
  • InvocationHandler:增强方法,添加额外的功能
  • MethodInterceptor:与 InvocationHandler 功能类似,但是控制的权限更多
  • LazyLoader:可以延迟加载被代理的对象,而且每个对象只会被创建一次
  • Dispatcher:与 LazyLoader 功能基本相同,但是在获取对象时,每次都会创建不同的对象
  • ProxyRefDispatcher:与 Dispatcher 功能类似,但是会多一个被代理对象的参数
  • NoOp:什么也不做

对于 FixedValue 类型的回调,调用所有的方法都会返回这个固定的值,如果类型不匹配,就会报错。

 
  

除了 static 和 final 类型的方法,其他所有的方法都会执行上面的代码,打印 。但是需要注意的是,如果某个方法返回的类型和上面的代理行为不一致就会报错,java.lang.Object 中的方法也是一样。

 
  

看到这个是不是很熟悉,这个与 JDK 反射自带的 InvocationHandler 基本一致。

 
  

只想改变其中部分方法的行为,其他的方法还的行为不变,最简单的思路就是如果不是目标方法就会调用本来的实现,假设要调用 方法:

 
  

这个代码并不会正常返回结果,而是进入无限循环,这是因为这个代理对象的每一个可以被代理的方法都被代理了,在调用被代理了的方法时,会重复进入到 这个方法中,然后进入死循环。

解决办法如下。

在 MethodInterceptor 中,有一个 参数,这个就可以用来执行父类方法。

 
  

用来延迟加载对象。

在下面这个例子中,使用 LazyLoader 来延迟加载一个 ArrayList 对象。

 
  

上面代码的执行结果如下:

 
  

在执行 data.get(0) 的时候,ArrayList 才会被创建,也就是说只有在被使用的,才会去创建对象,而且每个对象只会被创建一次。

Dispatcher 与 LazyLoader 的不同之处在于,每次去获取对象的时候都会创建一个新的对象,而不是复用同一个对象。

 
  

上面代理的执行结果如下,每次使用对象都会创建一个新的对象。

 
  

ProxyRefDispatcher 与 Dispatcher 功能类似,但是多了一个参数,使用这个回调同样要注意无限循环的问题。

 
  

这个回调什么也不做,会完全继承被代理类的功能,所以 NoOp 不能使用接口来创建代理,只能使用类。

 
  

这个回调在一些特定的情况下还是挺有用的,看下面的例子。

在上面的例子中,都是给 Enhancer 设置了单个回调,其实每个 Enhancer 可以设置多个回调,这里就需要用到 。

比如现在就需要对 方法进行处理,其他的方法保持父类的行为就可以,按照这个要求,实现的 CallbackFilter 如下:

 
  

调用结果如下,只有 sayHello 方法会被拦截,其他的方法不会有变动。

 
  

上面介绍了 Enhancer 之后,实现动态代理应该就不难了。

InvocationHandler 和 MethodInterceptor 都可以用来实现动态代理,下面是两种实现。

 
  

使用上面的实现来创建代理并调用方法:

 
  

这里需要注意,如果使用接口创建代理对象,第一行代码使用 或者 创建对象传入都可以,如果使用是父类创建代理对象,那么只能使用第二种。

 
  

然后创建代理对象并调用:

 
  

通常来说,使用 MethodInceptor 方式来实现更好,因为可以避免出现上面说到的无限循环的问题。

cglib 相比于 Java 反射实现动态代理的优势就是不受类的限制,可以自由的选择根据接口或者类来生成新的代理对象。

在 JDK 中,动态代理主要由 来实现,在 cglib 中,同样也实现了 Proxy,功能于 JDK 中的功能基本一致,其中用到的 就是前面介绍的回调。

这样做是为了 JDK1.3 以前的版本也能使用动态代理的功能。

这篇文章主要介绍了 cglib 的如果通过 Enhancer 去生成代理类,可以同时支持接口子类的两种方式。同时也介绍了通过 Enhancer 来实现动态代理。

cglib 的能力远不止这些,下篇文章将介绍 cglib 的其他功能。

[1] dzone.com/articles/cg…

文 / Rayjun

                                                  

版权声明


相关文章:

  • rgb225,225,0是什么颜色2025-08-29 19:30:03
  • python threadpool2025-08-29 19:30:03
  • data-toggle 菜鸟教程2025-08-29 19:30:03
  • 开窗函数语法2025-08-29 19:30:03
  • 神奇的代码2025-08-29 19:30:03
  • ajax请求异常怎么处理2025-08-29 19:30:03
  • java nio网络编程2025-08-29 19:30:03
  • rapiddirect2025-08-29 19:30:03
  • 地理学中尺度的概念2025-08-29 19:30:03
  • “免费的编程自学网站”2025-08-29 19:30:03