CGLib 是一个强大、高性能和高质量的代码生成库,它用于在运行时扩展 Java 类和实现接口。CGLib 被广泛应用于 AOP 框架中,如:Spring Framework 框架,用以提供方法拦截操作。Hibernate 作为一个比较受欢迎的 ORM(对象关系映射)框架,同样使用CGLib 来代理单端(多对一和一对一)关联。
CGLib 作为一个开源项目,其代码托管在 github,地址为:
https://github.com/cglib/cglib
CGLib 代理主要通过对字节码的操作,为对象引入间接级别,以控制对象的访问。
我们知道 Java 中有一个动态代理也是做这个事情的,那我们为什么不直接使用 Java 动态代理,而要使用 CGLib 呢?答案是 CGLib 相比于 JDK 动态代理更加强大。JDK 动态代理虽然简单易用,但是其有一个致命缺陷是,只能对接口进行代理。如果要代理的类为一个普通类、没有接口,那么 JDK 动态代理就没法使用了。
图1:CGLib 组成结构
CGLib 底层使用了 ASM(一个短小精悍的字节码操作框架)来操作字节码生成新的类。除了 CGLib 库外,脚本语言(如 Groovy 和 BeanShell)也使用 ASM 生成字节码。ASM 使用类似 SAX 的解析器来实现高性能。我们不鼓励直接使用 ASM,因为它需要对 Java 字节码的格式足够的了解。
上面说了这么多,你可能还是不知道 CGLib 是干什么的,下面通过一个具体的例子来介绍 CGLib 的用法。假如我们有一个 HelloWorld 类,该类有一个 test() 方法。然后,通过 CGLib 为 HelloWorld 创建一个代理对象,实现在调用 test() 方法前后打印字符串到控制台。代码如下:
(1)添加 Maven 依赖
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.3.0</version> </dependency>
(2)HelloWorld.java 类
/** * 被代理的类 * @author hxstrive.com 2021/12/29 */ public class HelloWorld { public void test(){ System.out.println("hello world"); } }
(3)CglibHello.java 类
import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; /** * 第一个 CGLib 示例,使用 CGLib 创建 {@link HelloWorld} 类的代理对象 * @author hxstrive.com 2021/12/28 */ public class CglibHello { public static void main(String[] args) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(HelloWorld.class); enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("before method run..."); Object result = proxy.invokeSuper(obj, args); System.out.println("after method run..."); return result; } }); HelloWorld sample = (HelloWorld) enhancer.create(); sample.test(); } }
运行程序,输出如下:
before method run... hello world after method run...