当前位置: 首页 > news >正文

【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动态代理区别&#xff08;面试常问&#xff09; 1. 简介 CGLIB的全称是&#xff1a;Code Generation Library。 CGLIB是一个强大的、高性能、高质量的代码生成类库&#xff0c;它可以在运行期扩展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安装应答文件时报错处理

环境&#xff1a;centos7 在执行packstack --answer-file./answer.ini命令后&#xff0c;一般需要几分钟才能完成&#xff0c;如何在applying IP controler.pp时报错&#xff0c;需要注意以下几点&#xff1a; 0.关闭firewalld和selinux&#xff08;必须&#xff09; system…...

SpringBoot整合MongoDB

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

线程同步机制与互斥锁

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

Python算法设计 - 编码加密

一、编码加密 编码加密应用十分广泛&#xff0c;特别是在大数据时代&#xff0c;也因此信息安全变得尤为重要 有时我会读到“OTP是一种无法被破解的加密方式”&#xff0c;当然&#xff0c;文末会附上一个完全被破解的OTP加密的例子 问题在于&#xff0c;人们经常会觉得完美的…...

数据结构和算法学习记录——平衡二叉树(基本介绍、平衡因子、平衡二叉树的定义、平衡二叉树的高度)

目录 基本介绍 平衡因子 平衡二叉树 平衡二叉树的高度 基本介绍 什么是平衡二叉树&#xff1f; 以一个例子来解释一下&#xff1a; 搜索树结点按不同的插入次序&#xff0c;将会导致不同的深度和平均查找长度ASL 在二叉搜索树中查找一个元素&#xff1a; &#xff08…...

【浓缩概率】浓缩概率思想帮我蒙选择题的概率大大提升!

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

两小时让你全方位的认识文件(一)

想必友友们在生活中经常会使用到各种各样的文件&#xff0c;那么我们是否了解它其中的奥秘呢&#xff0c;今天阿博就带领友友们深入地走入文件&#x1f6e9;️&#x1f6e9;️&#x1f6e9;️ 文章目录 一.为什么使用文件二.什么是文件三.文件的打开和关闭四.文件的顺序读写 一…...

基于Java+Springboot+vue网上商品订单转手系统设计和实现

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

旅游-商场购物

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

毕业论文用什么流程图软件比较好?

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

算法刷题|70.爬楼梯(进阶)、322.零钱兑换、279.完全平方数

爬楼梯&#xff08;进阶&#xff09; 题目&#xff1a;假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢&#xff1f; 思路&#xff1a;本题也可以抽象成完全背包的问题&#xff0c;背包就是总共多少阶台阶&am…...

【MCS-51】51单片机结构原理

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

软件测试技术之如何编写测试用例(3)

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

移远通信笔试题

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

python算法中的机器学习算法之监督学习知识点(详解)

目录 学习目标: 学习内容: Ⅰ. 线性回归(Linear Regression) Ⅱ. 逻辑回归(Logistic Regression)...

Flink主要有两种基础类型的状态:keyed state

Flink主要有两种基础类型的状态&#xff1a;keyed state 和operator state。 Keyed State Keyed State总是和keys相关&#xff0c;并且只能用于KeyedStream上的函数和操作。 你可以将Keyed State视为是已经被分片或分区的Operator State&#xff0c;每个key都有且仅有一个状态分…...

js录音支持h5 pc ios android

最近在做h5录音的页面要求可暂停录音,继续录音&#xff0c;写好后发现不兼容ios,无奈只能找兼容方法&#xff0c;找了一天也没找到&#xff0c;后来看到一个网站在ios上可以暂停录音&#xff0c;后来引入他的js文件果然能用了 网站放下面了 Recorder H5: 用于html5网页中的前…...

mybatis04-mybatis缓存、分页插件、注解开发(一对一、多对一、多对多)

mybatis04 mybatis 缓存 一、mybatis 缓存概述 1、缓存 ​ 缓存 是存在于内存中的临时数据&#xff0c;使用缓存的目的是&#xff1a;减少和数据库的交互次数&#xff0c;提高执行效率。 2、mybatis 缓存 ​ mybatis 与 大多数的持久层框架一样&#xff0c;提供了缓存策略…...

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...

DockerHub与私有镜像仓库在容器化中的应用与管理

哈喽&#xff0c;大家好&#xff0c;我是左手python&#xff01; Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库&#xff0c;用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...

基于ASP.NET+ SQL Server实现(Web)医院信息管理系统

医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上&#xff0c;开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识&#xff0c;在 vs 2017 平台上&#xff0c;进行 ASP.NET 应用程序和简易网站的开发&#xff1b;初步熟悉开发一…...

前端倒计时误差!

提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...

java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别

UnsatisfiedLinkError 在对接硬件设备中&#xff0c;我们会遇到使用 java 调用 dll文件 的情况&#xff0c;此时大概率出现UnsatisfiedLinkError链接错误&#xff0c;原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用&#xff0c;结果 dll 未实现 JNI 协…...

Opencv中的addweighted函数

一.addweighted函数作用 addweighted&#xff08;&#xff09;是OpenCV库中用于图像处理的函数&#xff0c;主要功能是将两个输入图像&#xff08;尺寸和类型相同&#xff09;按照指定的权重进行加权叠加&#xff08;图像融合&#xff09;&#xff0c;并添加一个标量值&#x…...

多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验

一、多模态商品数据接口的技术架构 &#xff08;一&#xff09;多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如&#xff0c;当用户上传一张“蓝色连衣裙”的图片时&#xff0c;接口可自动提取图像中的颜色&#xff08;RGB值&…...

Nuxt.js 中的路由配置详解

Nuxt.js 通过其内置的路由系统简化了应用的路由配置&#xff0c;使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...

让AI看见世界:MCP协议与服务器的工作原理

让AI看见世界&#xff1a;MCP协议与服务器的工作原理 MCP&#xff08;Model Context Protocol&#xff09;是一种创新的通信协议&#xff0c;旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天&#xff0c;MCP正成为连接AI与现实世界的重要桥梁。…...

如何在最短时间内提升打ctf(web)的水平?

刚刚刷完2遍 bugku 的 web 题&#xff0c;前来答题。 每个人对刷题理解是不同&#xff0c;有的人是看了writeup就等于刷了&#xff0c;有的人是收藏了writeup就等于刷了&#xff0c;有的人是跟着writeup做了一遍就等于刷了&#xff0c;还有的人是独立思考做了一遍就等于刷了。…...