Cglib 动态代理实现

news2024/11/27 10:27:35

每天看看新东西,心情也要好上许多

问题

  1. cglib是如何实现动态代理的
  2. cglib如何支持类的代理
  3. cglib和jdk的动态代理有什么区别

使用方式

cglib不属于jdk的一部分,因此要使用需要先引入相应的包,maven依赖如下

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.0</version>
</dependency>

另外因为spring的aop有使用cglib的实现,因此如果项目引入了spring-core包,那么spring-core包下面已经包含了cglib

使用例子

Test.java

public class Test {

    public static void main(String args[]){
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(TestCglib.class);
        enhancer.setCallback(new CglibMethodInterceptor());
        TestCglib testCglib = (TestCglib)enhancer.create();
        testCglib.sayBye("asdsd");
    }
}

TestCglib.java

public class TestCglib {
    public String sayHi(String str){
        System.out.println("我是被代理的sayHi方法");
        return str;
    }

    public String sayBye(String str){
        System.out.println("我是被代理的sayBye方法");
        return str;
    }
}

CglibMethodInterceptor.java

public class CglibMethodInterceptor implements MethodInterceptor {
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println(method.getName()+"执行之前做一些准备工作");
        Object result = methodProxy.invokeSuper(o,objects);
        System.out.println(method.getName()+"执行之后做一些准备的工作");
        return result;
    }
}

实现

和jdk的代理一样,我们先来看看生成的代理类长什么样:

TestCglib$$EnhancerByCGLIB$$8b20fd94.class(代理类)

public class TestCglib$$EnhancerByCGLIB$$8b20fd94 extends TestCglib implements Factory {
    private static final Method CGLIB$sayHi$1$Method;
    private static final MethodProxy CGLIB$sayHi$1$Proxy;


static void CGLIB$STATICHOOK1() {
    Class var0 = Class.forName("TestCglib$$EnhancerByCGLIB$$8b20fd94");
    Class var1;
    Method[] var10000 = ReflectUtils.findMethods(new String[]{"sayBye", "(Ljava/lang/String;)Ljava/lang/String;", "sayHi", "(Ljava/lang/String;)Ljava/lang/String;"}, (var1 = Class.forName("TestCglib")).getDeclaredMethods());
    CGLIB$sayHi$1$Method = var10000[1];
    CGLIB$sayHi$1$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/String;)Ljava/lang/String;", "sayHi", "CGLIB$sayHi$1");
}

final String CGLIB$sayHi$1(String var1) {
    return super.sayHi(var1);
}

public final String sayHi(String var1) {
    MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
    if (var10000 == null) {
        CGLIB$BIND_CALLBACKS(this);
        var10000 = this.CGLIB$CALLBACK_0;
    }

    return var10000 != null ? (String)var10000.intercept(this, CGLIB$sayHi$1$Method, new Object[]{var1}, CGLIB$sayHi$1$Proxy) : super.sayHi(var1);
}

}

TestCglib$$EnhancerByCGLIB$$8b20fd94$$FastClassByCGLIB$$2ada1598(代理类的FastClass类)

public class TestCglib$$EnhancerByCGLIB$$8b20fd94$$FastClassByCGLIB$$2ada1598 extends FastClass {
public TestCglib$$EnhancerByCGLIB$$8b20fd94$$FastClassByCGLIB$$2ada1598(Class var1) {
    super(var1);
}

public int getIndex(Signature var1) {
    String var10000 = var1.toString();
    switch(var10000.hashCode()) {
    case 481426893:
        if (var10000.equals("sayHi(Ljava/lang/String;)Ljava/lang/String;")) {
            return 7;
        }
        break;
    case 2082116023:
        if (var10000.equals("CGLIB$sayHi$1(Ljava/lang/String;)Ljava/lang/String;")) {
            return 11;
        }
    }

    return -1;
}


public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
    8b20fd94 var10000 = (8b20fd94)var2;
    int var10001 = var1;

    try {
        switch(var10001) {
        case 7:
            return var10000.sayHi((String)var3[0]);
        case 11:
            return var10000.CGLIB$sayHi$1((String)var3[0]);
        }
    } catch (Throwable var4) {
        throw new InvocationTargetException(var4);
    }

    throw new IllegalArgumentException("Cannot find matching method/constructor");
}

}

TestCglib$$FastClassByCGLIB$$2f8a820f(原始类的FastClass类)

public class TestCglib$$FastClassByCGLIB$$2f8a820f extends FastClass {
public TestCglib$$FastClassByCGLIB$$2f8a820f(Class var1) {
    super(var1);
}

public int getIndex(Signature var1) {
    String var10000 = var1.toString();
    switch(var10000.hashCode()) {
    case 481426893:
        if (var10000.equals("sayHi(Ljava/lang/String;)Ljava/lang/String;")) {
            return 1;
        }
        break;

    return -1;
}

public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
    TestCglib var10000 = (TestCglib)var2;
    int var10001 = var1;

    try {
        switch(var10001) {
        case 1:
            return var10000.sayHi((String)var3[0]);
        }
    } catch (Throwable var4) {
        throw new InvocationTargetException(var4);
    }

    throw new IllegalArgumentException("Cannot find matching method/constructor");
}

}

可以看到上面一共生成了三个类,为了突出重点我只保留了和sayHi这个方法相关的部分,这三个类的作用如下:

  1. 代理类
  2. 代理类的FastClass类
  3. 原始类的FastClass类

其中FastClass的类的作用是提前将需要执行的类的所有方法排序,为每个方法生成index,后续直接通过index定位方法执行调用,相对反射而言减少了查询方法的步骤。

ps:由实现可见一共有两个fastClass类,分别对应原始类和代理类,其使用场景分别对应MethodProxy(方法代理类)的invoke 和 invokeSuper类,这两个方法的区别是当调用invoke的时候会直接使用原始类来执行方法,而使用invokeSuper时是采用的代理类执行方法

假设我们访问的方法是sayHi,其调用过程用语言描述如下:

  1. 调用代理类的sayHi方法,代理类中会为每个方法生成一个方法代理类
  2. 代理类将方法调用转发至拦截器的intercept方法
  3. 拦截器在intercept方法中调用sayHi方法代理类invokeSuper 方法
  4. 方法代理类 使用代理类的FastClass类调用代理类的CGLIB$sayHi$1 方法
  5. 代理类调用super.sayHi()
  6. 执行原始类的sayHi方法

流程图如下:

 

结论

Q:cglib是如何实现动态代理的

利用asm在运行时动态生成和注册代理类,代理类采用继承的方式实现原始类的所有接口,并为每个接口实现方法代理,当调用代理类的方法时会先将所有请求转发至拦截器,后在拦截器中调用方法代理,在方法代理中再通过fastclass类回调回代理类

Q:cglib如何支持类的代理

生成的代理类会继承原始类

Q:cglib和jdk的动态代理有什么区别

  1. cglib 采用继承实现,jdk采用实现接口
  2. cglib 底层依赖asm,jdk不依赖

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/759830.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

TortoiseGit 入门指南09:合并

前面章节讲了 分支&#xff0c;提到一种常用的工作流&#xff1a;将默认分支&#xff08;master&#xff09;设置为主分支&#xff0c;保存最新的、随时可以发布的版本&#xff0c;所有的新特性、BUG都在另一个称为特性分支上增加或修改&#xff0c;然后在一个合适点&#xff0…

Ubuntu下搭建Redis主从集群

目录 准备实例和配置 开启主从关系 测试配置 搭建的主从集群结构&#xff0c;只有主服务器与客户端进行写操作&#xff0c;通过主从同步数据&#xff0c;从服务器向客户端提供读操作 共包含三个节点&#xff0c;一个主节点&#xff0c;两个从节点。 这里我们会在同一台虚拟机…

【C++】多线程编程二(std::thread详解)

目录 std::thread详解 &#xff08;1&#xff09;启动线程 ①无参无返回的函数作为入参 ②函数对象&#xff08;仿函数&#xff09;作为入参 &#xff08;2&#xff09;不等待线程detch() &#xff08;3&#xff09;等待线程完成join() &#xff08;4&#xff09;向线程…

CANoe如何配置Master/Slave模式

系列文章目录 文章目录 系列文章目录前言一、CANoe配置端口二、CANoe配置Master模式三、CANoe配置Slave模式前言 随着智能电动汽车的行业的发展,车载以太网的应用越来越广泛,最近很多朋友在问CANoe Master/Slave模式如何设置,车载以太网物理层也有一项是测试Master/Slave模式…

springcloud整合nacos实现注册发现中心

文章目录 微服务为什么需要服务注册发现中心怎么使用注册发现中心1.本示例环境2.nacos 安装3.pom.xml4.application.yml5.NacosDiscoveryDemoController6.ServerConfig7.NacosNacosDiscoveryServiceImpl8.启动用http工具测试结果 如果需要完整源码请关注公众号"架构殿堂&q…

2023.7.16-偶数(奇数)的枚举

功能&#xff1a;输入一个整数&#xff0c;结果打印出所有不大于这个整数的偶数。 程序&#xff1a; #define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> int main() {int a, b;printf("请输入一个整数&#xff1a;");scanf("%d",&a);print…

Android之Intent

意图介绍 一个意图(Intent)对象包含了目标组件、动作、数据、类别、附加数据、标志六个部分。 目标组件 目标组件可以帮助应用发送显式意图调用请求。在创建Intent时&#xff0c;可以通过setComponent方法来设置一个组件&#xff0c;如&#xff1a; //设置组件 intent.setC…

19.基于XML的自动装配

基于XML的自动装配 自动装配&#xff1a; 根据指定的策略&#xff0c;在IOC容器中匹配某一个bean&#xff0c;自动为指定的bean中所依赖的类类型或接口类型属性赋值 使用bean标签的autowire属性设置自动装配效果(默认为no和defalse不进行装配——bean中的属性不会自动匹配某个b…

Nodejs的字节操作(Buffer)

Hi I’m Shendi Nodejs的字节操作&#xff08;Buffer&#xff09; 字节操作是一个编程语言中必不可少的&#xff0c;而在NodeJs中也可以很方便的进行字节操作。 Buffer类 在 js 中没有二进制数据类型&#xff0c;但在一些情况下必须使用到二进制数据类型&#xff0c;比如网络通…

ModaHub魔搭社区:AI原生云向量数据库Zilliz Cloud与 OpenAI 集成搭建相似性搜索系统

目录 准备工作 检索图书 本文将讨论如何使用 OpenAI 的 Embedding API 与 Zilliz Cloud 搭建相似性搜索系统。 在本篇中你将看到如何使用 OpenAI 的 Embedding API 和 Zilliz Cloud 完成图书检索。当前,很多的图书检索方案,包括公共图书馆里使用的那些方案,都是使用关键词…

简单认识MySQL数据库事务

文章目录 一、MySQL事务的概念1、简介2、事务的ACID特点1.原子性&#xff08;Atomicity&#xff09;2.一致性&#xff08;Consistency&#xff09;3.隔离性&#xff08;lsolation&#xff09;4.持久性&#xff08;Durability) 3、并发访问表的一致性问题和事务的隔离级别1.并发…

C# Modbus通信从入门到精通(6)——Modbus RTU(0x04功能码)

1、04(0x04)读输入寄存器 使用该功能码能从远程地址中读取1到125个输入寄存器的值,每个输入寄存器都占两个字节,读取的输入寄存器数量由主站读取时指定。 2、发送报文格式 更详细的格式如下: 从站地址+功能码+起始地址高位+起始地址低位+寄存器数量高位+寄存器数量低位+…

备战求战 | 笔试强训6

目录 一、选择题 二、编程题 三、选择题题解 四、编程题题解 一、选择题 1、十进制变量i的值为100&#xff0c;那么八进制的变量i的值为&#xff08;&#xff09; A. 146 B. 148 C. 144 D. 142 2、执行下面语句后的输出为 int I1; if(I<0)printf("****\n") …

全志F1C200S嵌入式驱动开发(串口驱动)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】 对于uboot、kernel和rootfs来说,他们的串口输出一般都是uart0。一般这么做,是没有问题的。只不过我们自己买的这块f1c200s电路板,设计者把uart转ttl的接口,改接到了uart1上面。…

自定义注解+AOP完成公共字段填充

在开发中&#xff0c;我们的实体类经常会有几个公共的字段&#xff0c;如下图的创建时间&#xff0c;修改时间就为各个类共有的字段&#xff1a; 目前项目中处理这些字段都是在每一个业务方法中进行赋值操作,如下: 如果都按照上述的操作方式来处理这些公共字段, 需要在每一个业…

中级课程——信息收集(完结)

文章目录 概要whois备案号警告CDN历史解析查询子域名查询后台目录指纹C段&#xff0c;旁站真实IP与CDN端口证书其他资产社工漏洞情报实操案例 概要 whois 备案号 警告 CDN历史解析查询 子域名查询 工具推荐 或者找在线工具 后台目录 指纹 C段&#xff0c;旁站 真实IP与CDN 端…

无符号数和数据类型转换

无符号数 字符类型的无符号值&#xff1a; 所有的数据底层都是采用二进制来保存&#xff0c;而第一位用于保存符号位&#xff0c;当不考虑符号位时&#xff0c;所有的数都按照数值进行保存 #include <stdio.h>int main() {unsigned char a -65;printf("%u"…

JVM结构-堆

堆不是一种数据结构&#xff08;什么结构都可以存放在堆中&#xff09;堆内存的大小是可以调节的。类加载器读取了类文件后&#xff0c;需要把类、方法、常变量放到堆内存中&#xff0c;保存所有引用类型的真实信息JVM堆被同一个JVM实例中的所有Java线程共享JVM堆通常由某种自动…

Redis的过期策略以及内存淘汰机制

目录 一、过期策略1.1、定时删除1.1.1、过期1.1.2、过期的 key 集合1.1.3、定时扫描策略1.1.4、 Redis 中所有的 key 在同一时间过期了&#xff0c;会出现怎样的结果1.1.5、从库的过期策略 1.2、惰性删除1.3、定时删除和惰性删除的总结 二、缓存淘汰算法2.1、缓存淘汰算法概述2…

mysql 备份

mysql 备份 1. 备份的类型2.备份的类容3. 备份工具1&#xff1a;MySQL自带的备份工具2&#xff1a;文件系统备份工具3&#xff1a;其他工具 4:备份的策略1. 直接拷贝数据库文件2. mysqldump备份数据库3. mydumper备份数据库4.lvm快照从物理角度实现几乎热备的完全备份&#xff…