CGLIB介绍与原理

article/2025/10/31 16:10:24

一、什么是 CGLIB?

CGLIB是一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。通常可以使用Java的动态代理创建代理,但当要代理的类没有实现接口或者为了更好的性能,CGLIB是一个好的选择。

CGLIB作为一个开源项目,其代码托管在github,地址为:https://github.com/cglib/cglib


二、CGLIB 原理

CGLIB 原理:动态生成一个要代理类的子类,子类重写要代理的类的所有不是final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。它比使用java反射的JDK动态代理要快。

CGLIB 底层:使用字节码处理框架ASM,来转换字节码并生成新的类。不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。

CGLIB缺点:对于final方法,无法进行代理。


三、CGLIB 的应用

广泛的被许多AOP的框架使用,例如Spring AOP和dynaop。Hibernate使用CGLIB来代理单端single-ended(多对一和一对一)关联。


四、为什么使用 CGLIB?

CGLIB代理主要通过对字节码的操作,为对象引入间接级别,以控制对象的访问。我们知道Java中有一个动态代理也是做这个事情的,那我们为什么不直接使用Java动态代理,而要使用CGLIB呢?答案是CGLIB相比于JDK动态代理更加强大,JDK动态代理虽然简单易用,但是其有一个致命缺陷是,只能对接口进行代理。如果要代理的类为一个普通类、没有接口,那么Java动态代理就没法使用了。


五、CGLIB组成结构

CGLIB底层使用了ASM(一个短小精悍的字节码操作框架)来操作字节码生成新的类。除了CGLIB库外,脚本语言(如Groovy何BeanShell)也使用ASM生成字节码。ASM使用类似SAX的解析器来实现高性能。我们不鼓励直接使用ASM,因为它需要对Java字节码的格式足够的了解。


六、CGLIB的API

1、Jar包:

  • cglib-nodep-2.2.jar:使用nodep包不需要关联asm的jar包,jar包内部包含asm的类.
  • cglib-2.2.jar:使用此jar包需要关联asm的jar包,否则运行时报错.

2、CGLIB类库:

由于基本代码很少,学起来有一定的困难,主要是缺少文档和示例,这也是CGLIB的一个不足之处。

本系列使用的CGLIB版本是2.2。

  • net.sf.cglib.core: 底层字节码处理类,他们大部分与ASM有关系。
  • net.sf.cglib.transform: 编译期或运行期类和类文件的转换
  • net.sf.cglib.proxy: 实现创建代理和方法拦截器的类
  • net.sf.cglib.reflect: 实现快速反射和C#风格代理的类
  • net.sf.cglib.util: 集合排序等工具类
  • net.sf.cglib.beans: JavaBean相关的工具类

本篇介绍通过MethodInterceptor和Enhancer实现一个动态代理。

一、首先说一下JDK中的动态代理:

JDK中的动态代理是通过反射类Proxy以及InvocationHandler回调接口实现的,但是,JDK中所要进行动态代理的类必须要实现一个接口,也就是说只能对该类所实现接口中定义的方法进行代理,这在实际编程中具有一定的局限性,而且使用反射的效率也并不是很高。

二、使用CGLib实现:

使用CGLib实现动态代理,完全不受代理类必须实现接口的限制,而且CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类。

下面,将通过一个实例介绍使用CGLib实现动态代理。

1、被代理类:

首先,定义一个类,该类没有实现任何接口。

public class TargetObject {public String method1(String paramName) {return paramName;}public int method2(int count) {return count;}public int method3(int count) {return count;}@Overridepublic String toString() {return "TargetObject []"+ getClass();}
}

2、拦截器:

定义一个拦截器。在调用目标方法时,CGLib会回调MethodInterceptor接口方法拦截,来实现你自己的代理逻辑,类似于JDK中的InvocationHandler接口。

public class TargetInterceptor implements MethodInterceptor{@Overridepublic Object intercept(Object obj, Method method, Object[] params,MethodProxy proxy) throws Throwable {System.out.println("调用前");Object result = proxy.invokeSuper(obj, params);System.out.println("调用后"+result);return result;}}

参数:Object为由CGLib动态生成的代理类实例,Method为上文中实体类所调用的被代理的方法引用,Object[]为参数值列表,MethodProxy为生成的代理类对方法的代理引用。

返回:从代理实例的方法调用返回的值。

其中,proxy.invokeSuper(obj,arg) 调用代理类实例上的proxy方法的父类方法(即实体类TargetObject中对应的方法)

在这个示例中,只在调用被代理类方法前后各打印了一句话,当然实际编程中可以是其它复杂逻辑。

3、生成动态代理类:

public class TestCglib {public static void main(String args[]) {Enhancer enhancer =new Enhancer();enhancer.setSuperclass(TargetObject.class);enhancer.setCallback(new TargetInterceptor());TargetObject targetObject2=(TargetObject)enhancer.create();System.out.println(targetObject2);System.out.println(targetObject2.method1("mmm1"));System.out.println(targetObject2.method2(100));System.out.println(targetObject2.method3(200));}
}

这里Enhancer类是CGLib中的一个字节码增强器,它可以方便的对你想要处理的类进行扩展,以后会经常看到它。

首先将被代理类TargetObject设置成父类,然后设置拦截器TargetInterceptor,最后执行enhancer.create()动态生成一个代理类,并从Object强制转型成父类型TargetObject。

最后,在代理类上调用方法。

4、回调过滤器CallbackFilter

一、作用

在CGLib回调时可以设置对不同方法执行不同的回调逻辑,或者根本不执行回调。

在JDK动态代理中并没有类似的功能,对InvocationHandler接口方法的调用对代理类内的所以方法都有效。

定义实现过滤器CallbackFilter接口的类:

public class TargetMethodCallbackFilter implements CallbackFilter {@Overridepublic int accept(Method method) {if(method.getName().equals("method1")){System.out.println("filter method1 == 0");return 0;}if(method.getName().equals("method2")){System.out.println("filter method2 == 1");return 1;}if(method.getName().equals("method3")){System.out.println("filter method3 == 2");return 2;}return 0;}}

其中return值为被代理类的各个方法在回调数组Callback[]中的位置索引(见下文)。

public class TestCglib {public static void main(String args[]) {Enhancer enhancer =new Enhancer();enhancer.setSuperclass(TargetObject.class);CallbackFilter callbackFilter = new TargetMethodCallbackFilter();/*** (1)callback1:方法拦截器(2)NoOp.INSTANCE:这个NoOp表示no operator,即什么操作也不做,代理类直接调用被代理的方法不进行拦截。(3)FixedValue:表示锁定方法返回值,无论被代理类的方法返回什么值,回调方法都返回固定值。*/Callback noopCb=NoOp.INSTANCE;Callback callback1=new TargetInterceptor();Callback fixedValue=new TargetResultFixed();Callback[] cbarray=new Callback[]{callback1,noopCb,fixedValue};//enhancer.setCallback(new TargetInterceptor());enhancer.setCallbacks(cbarray);enhancer.setCallbackFilter(callbackFilter);TargetObject targetObject2=(TargetObject)enhancer.create();System.out.println(targetObject2);System.out.println(targetObject2.method1("mmm1"));System.out.println(targetObject2.method2(100));System.out.println(targetObject2.method3(100));System.out.println(targetObject2.method3(200));}
}
public class TargetResultFixed implements FixedValue{/*** 该类实现FixedValue接口,同时锁定回调值为999* (整型,CallbackFilter中定义的使用FixedValue型回调的方法为getConcreteMethodFixedValue,该方法返回值为整型)。*/@Overridepublic Object loadObject() throws Exception {System.out.println("锁定结果");Object obj = 999;return obj;}}

5.延迟加载对象

一、作用:

说到延迟加载,应该经常接触到,尤其是使用Hibernate的时候,本篇将通过一个实例分析延迟加载的实现方式。 LazyLoader接口继承了Callback,因此也算是CGLib中的一种Callback类型。

另一种延迟加载接口Dispatcher。

Dispatcher接口同样继承于Callback,也是一种回调类型。

但是Dispatcher和LazyLoader的区别在于:LazyLoader只在第一次访问延迟加载属性时触发代理类回调方法,而Dispatcher在每次访问延迟加载属性时都会触发代理类回调方法。

二、示例:

首先定义一个实体类LoaderBean,该Bean内有一个需要延迟加载的属性PropertyBean。

public class LazyBean {private String name;private int age;private PropertyBean propertyBean;private PropertyBean propertyBeanDispatcher;public LazyBean(String name, int age) {System.out.println("lazy bean init");this.name = name;this.age = age;this.propertyBean = createPropertyBean();this.propertyBeanDispatcher = createPropertyBeanDispatcher();}/*** 只第一次懒加载* @return*/private PropertyBean createPropertyBean() {/*** 使用cglib进行懒加载 对需要延迟加载的对象添加代理,在获取该对象属性时先通过代理类回调方法进行对象初始化。* 在不需要加载该对象时,只要不去获取该对象内属性,该对象就不会被初始化了(在CGLib的实现中只要去访问该对象内属性的getter方法,* 就会自动触发代理类回调)。*/Enhancer enhancer = new Enhancer();enhancer.setSuperclass(PropertyBean.class);PropertyBean pb = (PropertyBean) enhancer.create(PropertyBean.class,new ConcreteClassLazyLoader());return pb;}/*** 每次都懒加载* @return*/private PropertyBean createPropertyBeanDispatcher() {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(PropertyBean.class);PropertyBean pb = (PropertyBean) enhancer.create(PropertyBean.class,new ConcreteClassDispatcher());return pb;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public PropertyBean getPropertyBean() {return propertyBean;}public void setPropertyBean(PropertyBean propertyBean) {this.propertyBean = propertyBean;}public PropertyBean getPropertyBeanDispatcher() {return propertyBeanDispatcher;}public void setPropertyBeanDispatcher(PropertyBean propertyBeanDispatcher) {this.propertyBeanDispatcher = propertyBeanDispatcher;}@Overridepublic String toString() {return "LazyBean [name=" + name + ", age=" + age + ", propertyBean="+ propertyBean + "]";}
}
public class PropertyBean {private String key;private Object value;public String getKey() {return key;}public void setKey(String key) {this.key = key;}public Object getValue() {return value;}public void setValue(Object value) {this.value = value;}@Overridepublic String toString() {return "PropertyBean [key=" + key + ", value=" + value + "]" +getClass();}}
public class ConcreteClassLazyLoader implements LazyLoader {/*** 对需要延迟加载的对象添加代理,在获取该对象属性时先通过代理类回调方法进行对象初始化。* 在不需要加载该对象时,只要不去获取该对象内属性,该对象就不会被初始化了(在CGLib的实现中只要去访问该对象内属性的getter方法,* 就会自动触发代理类回调)。*/@Overridepublic Object loadObject() throws Exception {System.out.println("before lazyLoader...");PropertyBean propertyBean = new PropertyBean();propertyBean.setKey("zghw");propertyBean.setValue(new TargetObject());System.out.println("after lazyLoader...");return propertyBean;}}
public class ConcreteClassDispatcher implements Dispatcher{@Overridepublic Object loadObject() throws Exception {System.out.println("before Dispatcher...");PropertyBean propertyBean = new PropertyBean();propertyBean.setKey("xxx");propertyBean.setValue(new TargetObject());System.out.println("after Dispatcher...");return propertyBean;}}

测试调用:

public class TestCglib3 {public static void main(String[] args) {LazyBean lazybean = new LazyBean("wjy", 33);PropertyBean pb1 = lazybean.getPropertyBean();System.out.println("key =" + pb1.getKey() + ", value = " + pb1.getValue());PropertyBean pb2 = lazybean.getPropertyBeanDispatcher();System.out.println("key =" + pb2.getKey() + ", value = " + pb2.getValue());}}

6.接口生成器InterfaceMaker

一、作用:

InterfaceMaker会动态生成一个接口,该接口包含指定类定义的所有方法。

二、示例:

public class TestInterfaceMaker {public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {InterfaceMaker interfaceMaker =new InterfaceMaker();//抽取某个类的方法生成接口方法interfaceMaker.add(TargetObject.class);Class<?> targetInterface=interfaceMaker.create();for(Method method : targetInterface.getMethods()){System.out.println(method.getName());}//接口代理并设置代理接口方法拦截Object object = Enhancer.create(Object.class, new Class[]{targetInterface}, new MethodInterceptor(){@Overridepublic Object intercept(Object obj, Method method, Object[] args,MethodProxy methodProxy) throws Throwable {if(method.getName().equals("method1")){System.out.println("filter method1 ");return "111111";}if(method.getName().equals("method2")){System.out.println("filter method2 ");return 222222;}if(method.getName().equals("method3")){System.out.println("filter method3 ");return 333333;}return "default";}});Method targetMethod1=object.getClass().getMethod("method3",new Class[]{int.class});int i=(int)targetMethod1.invoke(object, new Object[]{33});Method targetMethod=object.getClass().getMethod("method1",new Class[]{String.class});System.out.println(targetMethod.invoke(object, new Object[]{"sdfs"}));}
}

 

 


http://chatgpt.dhexx.cn/article/4O8ubO0E.shtml

相关文章

CGLIB(Code Generation Library)详解

什么是CGLIB CGLIB是一个强大的、高性能的代码生成库。其被广泛应用于AOP框架&#xff08;Spring、dynaop&#xff09;中&#xff0c;用以提供方法拦截操作。Hibernate作为一个比较受欢迎的ORM框架&#xff0c;同样使用CGLIB来代理单端&#xff08;多对一和一对一&#xff09;…

【动态代理】CGLIB 动态代理的使用及原理

1. CGLIB 动态代理介绍 什么是 CGLIB&#xff1f; CGLIB是一个功能强大&#xff0c;高性能的代码生成包。它为没有实现接口的类提供代理&#xff0c;为JDK的动态代理提供了很好的补充。 通常可以使用Java的动态代理创建代理&#xff0c;但当要代理的类没有实现接口或者为了更…

CGLIB详解(最详细)

转载地址:https://blog.csdn.net/danchu/article/details/70238002 什么是CGLIB CGLIB是一个强大的、高性能的代码生成库。其被广泛应用于AOP框架&#xff08;Spring、dynaop&#xff09;中&#xff0c;用以提供方法拦截操作。Hibernate作为一个比较受欢迎的ORM框架&#xff0c…

GPG error解决方案

问题: sudo apt-get update时报错GPG error 解决方案: // F42ED6FBAB17C654是根据你报错那一行确定的 sudo gpg --keyserver keyserver.ubuntu.com --recv F42ED6FBAB17C654 sudo gpg --export --armor F42ED6FBAB17C654 | sudo apt-key add -然后: sudo apt-get update

GPG Keys are configured as: file:///etc/pki/rpm-gpg/RPM-GPG-KEY-mysql

阿里云CentOS 7.9 64位 搭建网站踩坑实录 问题1.GPG Keys are configured as: file:///etc/pki/rpm-gpg/RPM-GPG-KEY-mysql Failing package is: mysql-community-libs-compat-5.7.37-1.el7.x86_64 GPG Keys are configured as: file:///etc/pki/rpm-gpg/RPM-GPG-KEY-mysql …

gpg文件加密解密

使用Ubuntu对文件进行gpg格式的加密和解密 安装 下载源码安装  ./configure   make   make install 命令安装 Debian环境 sudo apt-get install gnupg Fedora 环境 yum install gnupg 加密 gpg -c abc.txt 会让输入一个加密的密码&#xff0c;需要自己输入 解密 gpg -o …

GPG(GnuPG)的安装和使用

基于网络的开源项目&#xff0c;能给用户带来在公共标准基础上的自由发挥&#xff0c;并且能很好地给每个自愿人士提供了共享贡献的机会。但是&#xff0c;同时也因为大众化给使用共享的程序员或团队带来了安全性问题。 当程序员从中央仓库下载第三方构件的时候&#xff0c;下载…

gpg使用

https://blog.csdn.net/weixin_42559321/article/details/82147888 https://www.cnblogs.com/wanghongli/archive/2018/01/08/8241809.html rpm2cpio *.rpm | cpio -imd       #解压一个rpm包rpm -ivh *.rpm --force       #强制安装这个rpm包rpm -iv…

如何在Git中使用GPG

开篇之前&#xff0c;先给大伙看点东西 是不是很想要&#xff1f;你找对地方了! 下面是教程&#xff1a; 在 “开始”菜单 打开Git Bash 输入 gpg --gen-key 显示如下 $ gpg --gen-keygpg (GnuPG) 2.2.13-unknown; Copyright (C) 2019 Free Software Foundation, Inc.This …

成功解决gpg: 找不到有效的 OpenPGP 数据

在Ubuntu系统上安装docker时出现gpg: 找不到有效的 OpenPGP 数据的报错 解决方案&#xff1a; wget https://download.docker.com/linux/ubuntu/gpg sudo apt-key add gpg随后再次执行下载指令&#xff0c;解决报错 成功解决gpg: 找不到有效的 OpenPGP 数据的报错 欢迎小伙…

GPG Overview

Overview PGP目前支持的算法 非对称算法: RSA, ELG, DSA, ECDH, ECDSA, EDDSA对称算法: IDEA, 3DES, CAST5, BLOWFISH, AES, AES192, AES256, TWOFISH, CAMELLIA128, CAMELLIA192, CAMELLIA256哈希算法: SHA1, RIPEMD160, SHA256, SHA384, SHA512, SHA224压缩算法: Uncompres…

GPG 使用初步

GPG 使用初步 1. PGP 软件的安装 PGP 的版本有很多&#xff0c;但由于其商业软件的特性&#xff0c;不能自由使用&#xff0c;自由软件基金会决定开发一个 PGP 的替代品&#xff0c;取名为 GnuPG &#xff0c;这就是 PGP 的由来   GPG 是基于命令行的程序&#xff0c;主要面…

gpg加解密软件学习

为什么要学习gpg呢&#xff1f;因为要在Linux下把一个邮箱的密码加密&#xff0c;不让其他人看到该邮箱真正的密码。 为了不让其他人看到真正的邮箱密码&#xff0c;我们需要对其进行加密。 加密的方式是先把密码先写到一个文件A中&#xff0c;然后使用相关的加密软件对该文件…

java动态代理

java动态代理实现与原理详细分析 原文地址 关于Java中的动态代理&#xff0c;我们首先需要了解的是一种常用的设计模式--代理模式&#xff0c;而对于代理&#xff0c;根据创建代理类的时间点&#xff0c;又可以分为静态代理和动态代理。 一、代理模式 代理模式是常用的java…

动态规划 --- 算法思想介绍

一.动态规划的基本概念 动态规划在五种算法设计方法中难度最大&#xff0c;它建立在最优原则的基础上.采用动态规划方法&#xff0c;可以高效地解决许多用贪婪算法或分治法无法解决的问题.动态规划(dynamic programming)属运筹学中的规划论分支&#xff0c;是求解决策过程最优…

动态规划算法详解

动态规划算法通常用于求解具有最优性质的问题 基本概念 动态规划过程是&#xff1a;每次决策依赖于当前状态&#xff0c;又随即引起状态的转移。一个决策序列就是在变化的状态中产生出来的&#xff0c;所以&#xff0c;这种多阶段最优化决策解决问题的过程就称为动态规划(DP)。…

动态规划原理

1. 基本概念 动态规划通过拆分问题&#xff0c;将问题拆分成许多的子问题&#xff0c;定义问题状态和状态之间的关系&#xff08;即状态转移方程或递推公式&#xff09;&#xff0c;使得问题能够以递推&#xff08;或者说分治&#xff09;的方式去解决。按顺序求解子问题&…

动态代理详解

想要更加透彻的理解动态代理&#xff0c;首先要熟悉下静态代理 一、静态代理 总结来说&#xff1a;目标类和代理类实现了相同的接口&#xff0c;在代理类中依赖了目标类&#xff0c;代理类的方法中调用了目标类的方法&#xff0c;并做了一些增强性的工作。 1、实现静态代理&…

CAD动态块制作

CAD动态块制作 拉伸动态块柜体A拉伸动态块制作第一步&#xff1a;制作柜体A第二步&#xff1a;进入块编辑器编辑第三步&#xff1a;关闭块编辑器 柜体B拉伸动态块制作第一步&#xff1a;制作柜体B第二步&#xff1a;进入块编辑器第三步&#xff1a;关闭块编辑器 可见性动态块第…

数据有效性 动态选择

EXCEL有两列数据栏&#xff0c;A列和B列&#xff0c;都是通过下来框来选择&#xff0c;要求A列选择完成后&#xff0c;B列动态调整可选择的内容。例如&#xff1a;第一例选择“AA”&#xff0c;第二列可供选择的范围是“a-d”&#xff1b;第一例选择“BB”&#xff0c;第二列可…