【java】CGLIB动态代理原理
文章目录
- 1. 简介
- 2. 示例
- 3. 原理
- 4. JDK动态代理与CGLIB动态代理区别(面试常问)
1. 简介
CGLIB的全称是:Code Generation Library。
CGLIB是一个强大的、高性能、高质量的代码生成类库,它可以在运行期扩展Java类与实现Java接口,
底层使用的是字节码处理框架ASM。
Github地址:github.com/cglib/cglib。
CGLIB的Maven坐标如下所示:
<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.3.0</version>
</dependency>
2. 示例
首先,新增一个类:
public class Coder {public void work() {System.out.println("认真写bug……");}
}
然后,自定义一个方法拦截器,实现net.sf.cglib.proxy.MethodInterceptor接口并重写intercept方法:
public class AttendanceMethodInterceptor implements MethodInterceptor {@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("上班打卡……");Object result = proxy.invokeSuper(obj, args);System.out.println("下班打卡……");return result;}
}
重点看下Object result = proxy.invokeSuper(obj, args);
,该行代码最终会执行真正的目标方法,在这前后,我们可以添加一些增强逻辑。
然后,新建个测试类,看下CGLIB动态代理如何使用:
public class CglibProxyTest {public static void main(String[] args) {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(Coder.class);enhancer.setCallback(new AttendanceMethodInterceptor());// 创建代理对象Object object = enhancer.create();Coder coder = (Coder) object;coder.work();}
}
运行以上代码,效果如下图所示:
从运行结果可以看出,在目标方法的前后,执行了自定义的操作。
3. 原理
看下上面的测试类代码,首先是创建了一个net.sf.cglib.proxy.Enhancer
对象,然后调用了setSuperclass()
方法将enhancer
对象的父类设置为Coder类:
紧接着调用了setCallback()
方法将enhancer
对象的方法拦截器设置为自定义的AttendanceMethodInterceptor
:
然后是调用enhancer对象的create()方法来生成一个代理对象。
先打印下,简单看下这个代理类的信息:
图中的com.zwwhnly.mybatisplusdemo.cglibproxy.Coder$$EnhancerByCGLIB$$8e91f654
就是CGLIB生成的代理类的名称。
那么这个代理类具体是什么样子呢?
在上面的测试类代码中(Object object = enhancer.create();
代码之前)添加以下一行代码:
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "./cglib");
然后再次运行,会看到项目根目录下生成了一个cglib文件夹,自动生成的代理类就包含在其中:
可以看到一共生成了5个类,这里重点关注下红色标记的3个类。
先看下Coder$$EnhancerByCGLIB$$8e91f654.class
,这个类就是自动生成的代理类:
可以看出Coder$$EnhancerByCGLIB$$8e91f654.class
继承了Coder类(也就是说自动生成的代理类其实是被代理类的一个子类),并且重写了Coder类的work()方法,重写后的work()方法会调用自定义的方法拦截器AttendanceMethodInterceptor里的intercept()方法。
然后看下Coder$$EnhancerByCGLIB$$8e91f654$$FastClassByCGLIB$$4e5eb5aa
,从名称上可以看出这个类的前半段和上面的类的名称是一样的,后半段拼接上了$$FastClassByCGLIB$$4e5eb5aa
,从功能上说,这个类是上面的代理类的索引类,重点关注下里面的getIndex()方法和invoke()方法:
最后看下Coder$$FastClassByCGLIB$$398819d0
,这个类是被代理类Coder的索引类,重点也是关注下里面的getIndex()
方法和invoke()
方法:
知道了这3个类的作用后,再一步一步看下示例代码中coder.work();的调用过程,因为coder是生成的代理类的实例,所以coder.work();
首先调用的是Coder$$EnhancerByCGLIB$$8e91f654的work()
方法:
这里的var10000是自定义的方法拦截器AttendanceMethodInterceptor,所以执行的是红色截图里的intercept()
方法,也就是:
然后看下invokeSuper()
方法:
首先执行的是init()方法,在该方法内部对fastClassInfo字段进行了赋值:
从上图可以看出,fci.f1是自动生成的Coder类的索引类Coder$$FastClassByCGLIB$$398819d0,所以fci.i1 = fci.f1.getIndex(sig1);
其实执行的是的Coder$$FastClassByCGLIB$$398819d0的getIndex()
方法:
fci.f2是自动生成的代理类的索引Coder$$EnhancerByCGLIB$$8e91f654$$FastClassByCGLIB$$4e5eb5aa,
所以fci.i2 = fci.f2.getIndex(sig2);其实执行的是的Coder$$EnhancerByCGLIB$$8e91f654$$FastClassByCGLIB$$4e5eb5aa
的getIndex()方法:
看完init()
方法后再回到invokeSuper()
方法:
上图中的FastClassInfo fci = fastClassInfo;
使用到的字段fastClassInfo在init()方法内部已经赋过值,
fci.f2其实是自动生成的代理类的索引类Coder$$EnhancerByCGLIB$$8e91f654$$FastClassByCGLIB$$4e5eb5aa
,fci.i2值是1,
所以fci.f2.invoke(fci.i2, obj, args);
实际执行的是:
这里的var10000
其实是自动生成的代理类Coder$$EnhancerByCGLIB$$8e91f654
的实例,所以接着调用的是
代理类Coder$$EnhancerByCGLIB$$8e91f654的CGLIB$work$0()
方法:
这里的super指的是Coder类,所以super.work();实际执行的是Coder类的work()方法:
综上所述,coder.work();
的调用顺序依次是:
代理类—>自定义方法拦截器—>代理类索引类getIndex()方法–>代理类索引类invoke()方法—>代理类—>被代理类。
4. JDK动态代理与CGLIB动态代理区别(面试常问)
关于JDK动态代理,可以查看上一篇博客:JDK动态代理原理。
了解了JDK动态代理和CGLIB动态代理的原理后,现在来比较下两者的区别,这也是面试时几乎必问的一道面试题。
-
使用JDK动态代理,被代理类必须要实现接口,使用CGLIB动态代理,被代理类可以不实现接口
原因分析:
JDK动态代理生成的代理类继承了java.lang.reflect.Proxy
,因为Java是单继承的,如果不通过实现接口的形式,
无法对类进行扩展。
CGLIB动态代理生成的代理类实际上是被代理类的子类,所以被代理类可以不实现接口。 -
自动生成类的数量不同
JDK动态代理只会生成1个代理类,一般情况下名称为:
com.sun.proxy.$Proxy0
。
CGLIB动态代理会生成好几个类,核心的3个分别是:- 代理类:被代理类的子类,名称格式为
Coder$$EnhancerByCGLIB$$8e91f654
,包名和被代理类包名一致。 - 代理类的索引类:名称格式为
Coder$$EnhancerByCGLIB$$8e91f654$$FastClassByCGLIB$$4e5eb5aa
,
包名和被代理类包名一致。 - 被代理类的索引类:名称格式为
Coder$$FastClassByCGLIB$$398819d0
,包名和被代理类包名一致。
- 代理类:被代理类的子类,名称格式为
-
生成代理类技术不同
JDK动态代理使用JDK自带的ProxyGenerator类生成字节码文件。
CGLIB动态代理使用ASM框架生成字节码文件。 -
调用方式不同
JDK动态代理:代理类—>InvocationHandler.invoke()—>被代理类方法(用到了反射)。
CGLIB动态代理:代理类—>MethodInterceptor.intercept()—>代理类索引类getIndex()—>
代理类索引类invoke()—>代理类—>被代理类。(直接调用)
相关文章:

【java】CGLIB动态代理原理
文章目录 1. 简介2. 示例3. 原理4. JDK动态代理与CGLIB动态代理区别(面试常问) 1. 简介 CGLIB的全称是:Code Generation Library。 CGLIB是一个强大的、高性能、高质量的代码生成类库,它可以在运行期扩展Java类与实现Java接口&a…...

ArcGIS Pro、Python、USLE、INVEST模型等多技术融合的生态系统服务构建生态安全格局
第一章、生态安全评价理论及方法介绍 一、生态安全评价简介 二、生态服务能力简介 三、生态安全格局构建研究方法简介 第二章、平台基础一、ArcGIS Pro介绍1. ArcGIS Pro简介2. ArcGIS Pro基础3. ArcGIS Pro数据编辑4. ArcGIS Pro空间分析5. 模型构建器6. ArcGIS Pro…...

openstack安装应答文件时报错处理
环境:centos7 在执行packstack --answer-file./answer.ini命令后,一般需要几分钟才能完成,如何在applying IP controler.pp时报错,需要注意以下几点: 0.关闭firewalld和selinux(必须) system…...

SpringBoot整合MongoDB
文章目录 一、环境准备二、集合操作三、文档操作3.1 实体类3.2 添加文档3.3 查询文档3.4 修改文档3.5 删除文档 提示:以下是本篇文章正文内容,MongoDB 系列学习将会持续更新 一、环境准备 ①添加 SpringData 依赖: <dependency><…...

线程同步机制与互斥锁
线程同步机制 在多线程编程,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在任何时刻,最多有一个线程访问,以保证数据的完整性。也可以这里理解:线程同步,即当有一个线程在对内存…...

Python算法设计 - 编码加密
一、编码加密 编码加密应用十分广泛,特别是在大数据时代,也因此信息安全变得尤为重要 有时我会读到“OTP是一种无法被破解的加密方式”,当然,文末会附上一个完全被破解的OTP加密的例子 问题在于,人们经常会觉得完美的…...
数据结构和算法学习记录——平衡二叉树(基本介绍、平衡因子、平衡二叉树的定义、平衡二叉树的高度)
目录 基本介绍 平衡因子 平衡二叉树 平衡二叉树的高度 基本介绍 什么是平衡二叉树? 以一个例子来解释一下: 搜索树结点按不同的插入次序,将会导致不同的深度和平均查找长度ASL 在二叉搜索树中查找一个元素: (…...

【浓缩概率】浓缩概率思想帮我蒙选择题的概率大大提升!
今天在学习的时候遇到一个很有趣的思想叫作浓缩概率,可以帮我们快速解决一下概率悖论问题! 什么是概率 计算概率有下面两个最简单的原则: 原则一、计算概率一定要有一个参照系,称作「样本空间」,即随机事件可能出现…...

两小时让你全方位的认识文件(一)
想必友友们在生活中经常会使用到各种各样的文件,那么我们是否了解它其中的奥秘呢,今天阿博就带领友友们深入地走入文件🛩️🛩️🛩️ 文章目录 一.为什么使用文件二.什么是文件三.文件的打开和关闭四.文件的顺序读写 一…...

基于Java+Springboot+vue网上商品订单转手系统设计和实现
基于JavaSpringbootvue网上商品订单转手系统设计和实现 博主介绍:5年java开发经验,专注Java开发、定制、远程、指导等,csdn特邀作者、专注于Java技术领域 作者主页 超级帅帅吴 Java项目精品实战案例《500套》 欢迎点赞 收藏 ⭐留言 文末获取源码联系方式…...

旅游-商场购物
标题 前言必学场景词汇及用法售货员接待促销活动选购商品询问材质与质量试穿衣服杀价修改衣服结账售后服务退换货情境常用单词化妆品类别护肤品类别护肤品功能前言 加油 必学场景词汇及用法 售货员接待 1.be of service to sb 服务某人 Hello, ma’am. Could I be of serv…...

毕业论文用什么流程图软件比较好?
在写作论文的时候使用流程图,会让我们的论文看起来更加有逻辑。并且流程图的图片都可以在PPT中随意插入以及使用。 基础流程图作为最为基本和简单的的流程图方式,一般不区分用户角色和场景,适用于简单场景,梳理单一的流程情况&am…...

算法刷题|70.爬楼梯(进阶)、322.零钱兑换、279.完全平方数
爬楼梯(进阶) 题目:假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢? 思路:本题也可以抽象成完全背包的问题,背包就是总共多少阶台阶&am…...

【MCS-51】51单片机结构原理
至今为止,MCS-51系列单片机有许多种型号的产品:其中又分为普通型51(8031、8051、89S51)和增强型52(8032、8052、89S52等)。它们最大的区别在于存储器配置各有差异。下面我举例子的都是8051这一系列的单片机…...

软件测试技术之如何编写测试用例(3)
14、对于类似于手机版淘宝这种软件,它拥有客户端,服务器端还有一个后台管理系统类似于进销存管理系统,我如何设计测试用例才能保证功能的完全覆盖?他们之间的交互如何设计测试用例? 专家分析:对于复合型的…...

移远通信笔试题
限时60分钟 1.下列关于栈叙述正确的是 A A) 栈顶元素最先能被删除 B)栈顶元素最后才能被删除 C)栈底元素永远不能被删除 D)以上三种都不对 在栈中,最后被压入的元素总是在栈顶上方,而栈顶元素总是最先被弹出的元…...

python算法中的机器学习算法之监督学习知识点(详解)
目录 学习目标: 学习内容: Ⅰ. 线性回归(Linear Regression) Ⅱ. 逻辑回归(Logistic Regression)...

Flink主要有两种基础类型的状态:keyed state
Flink主要有两种基础类型的状态:keyed state 和operator state。 Keyed State Keyed State总是和keys相关,并且只能用于KeyedStream上的函数和操作。 你可以将Keyed State视为是已经被分片或分区的Operator State,每个key都有且仅有一个状态分…...
js录音支持h5 pc ios android
最近在做h5录音的页面要求可暂停录音,继续录音,写好后发现不兼容ios,无奈只能找兼容方法,找了一天也没找到,后来看到一个网站在ios上可以暂停录音,后来引入他的js文件果然能用了 网站放下面了 Recorder H5: 用于html5网页中的前…...

mybatis04-mybatis缓存、分页插件、注解开发(一对一、多对一、多对多)
mybatis04 mybatis 缓存 一、mybatis 缓存概述 1、缓存 缓存 是存在于内存中的临时数据,使用缓存的目的是:减少和数据库的交互次数,提高执行效率。 2、mybatis 缓存 mybatis 与 大多数的持久层框架一样,提供了缓存策略…...

软件平台接口常见问题汇总
接口常见问题汇总 一、接口技术层面 1、输入参数验证校验不全面。如: 1.1入参数据类型长度边界,范围边界。 1.2 入参数据内容、成员内容,有效无效,合法非法。 1.3 入参数据 特殊字符 敏感字符过滤。 1.4 入参可否必选。 2、接口…...

SparkStreaming学习之——无状态与有状态转化、遍历kafka的topic消息、WindowOperations
目录 一、状态转化 二、kafka topic A→SparkStreaming→kafka topic B (一)rdd.foreach与rdd.foreachPartition (二)案例实操1 1.需求: 2.代码实现: 3.运行结果 (三)案例实操2 1.需求: 2.代码实现: 3.运行结果 三、W…...

上市公司碳排放测算数据(1992-2022年)
根据《温室气体核算体系》,企业的碳排放可以分为三个范围。 范围一是直接温室气体排放,产生于企业拥有或控制的排放源,例如企业拥有或控制的锅炉、熔炉、车辆等产生的燃烧排放;拥有或控制的工艺设备进行化工生产所产生的排放。 范…...

Springboot 整合 JPA 及 Swagger2
首先是官方文档: Spring Data JPA - Reference Documentationhttps://docs.spring.io/spring-data/jpa/docs/2.2.4.RELEASE/reference/html/#repositories.query-methods 1、JPA相关概念 2、创建 Springboot 项目 修改 pom 文件,可以直接进行复制粘贴&a…...

android aidl
本文只是记录个人学习aidl的实现,如需学习请参考下面两篇教程 官方文档介绍Android 接口定义语言 (AIDL) | Android 开发者 | Android Developers 本文参考文档Android进阶——AIDL详解_android aidl_Yawn__的博客-CSDN博客 AIDL定义:Android 接口…...

MYSQL---主从同步概述与配置
一、MYSQL主从同步概述 1、什么是MySQL主从同步? 实现数据自动同步的服务结构 主服务器(master): 接受客户端访问连接 从服务器(slave):自动同步主服务器数据 2、主从同步原理 Maste:启用binlog 日志 Slave:Slave_IO: 复制master主…...

WebClient学习
1. 介绍 Java中传统的RestTemplate 的主要问题在于不支持响应式流规范,也就无法提供非阻塞式的流式操作。而WebClient是响应式、非阻塞的客户端,属于Spring5中的spring-webflux库 2. 依赖 maven依赖 <dependency><groupId>org.springfra…...

「计算机控制系统」6. 直接设计法
特殊类型系统的最小拍无差设计 一般系统的最小拍无差设计 最小拍控制器的工程化改进 Dahlin算法 文章目录 特殊类型系统的最小拍无差设计理论分析典型输入函数的最小拍无差系统 一般系统的最小拍无差设计有波纹最小拍无差设计无波纹最小拍无差设计 最小拍控制器的工程化改进针对…...

什么是JWT?
起源 需要了解一门技术,首先从为什么产生开始说起是最好的。JWT 主要用于用户登录鉴权,所以我们从最传统的 session 认证开始说起。 session认证 众所周知,http 协议本身是无状态的协议,那就意味着当有用户向系统使用账户名称和…...

STM32—0.96寸OLED液晶显示
本文主要介绍基于STM32F103的0.96寸的OLED液晶显示,详细关于0.96寸OLED液晶屏幕的介绍可参考这篇博客:https://blog.csdn.net/u011816009/article/details/130119426 一、简介 OLED被称为有机激光二极管,也被称为有机激光显示,O…...