第 13 篇《除了 JDK、CGLIB,还有 3 种类代理方式?面试又卡住!》

Spring源码核心解析:IoC容器、Bean生命周期与AOP原理实战 控制反转(IoC)是Spring框架的基石,它通过将对象的创建与依赖管理交由容器负责,实现了组件间的解耦。其核心原理在于BeanFactory与ApplicationContext构成的容器体系,它们读取配置元信息,解析为BeanDefinition,并管理Bean的完整生命周期——包括实例化、依赖注入、初始化及销毁。这一机制的技术价值在于提供了强大的可扩展性,通过BeanPostProcessor等扩展点,开发者可以干预Bean的创建过程,实现如AOP、事务管理等高级功能。在应用场景上,IoC容器广泛应用 阅读详情

一、前言

编程学习,先铺宽度还是挖深度?

其实技术宽度与技术深度是相辅相成的,你能了解多少技术是和你对一个技术的理解深度有关,而你能对一个技术探究的多深又是需要你有一定的广度认知。否则如果只去了解皮毛或者死磕一段代码,收获都不一定有多大,或者要付出很大的成本。

技术瓶颈,与年龄相关还是大厂?

亲身当过面试官很久,也面试过很多人。有时候不一定年龄很大就技术好,也不一定刚工作 2 年左右就不行。往往我们说的一些面试造火箭,但是在这些求职者的回答中,都能给出非常准确的答案。也就是他能回答到点上,这是懂了才能做到的。

工作时长与是否在大厂,这些都是能接触到资源的多少,看到技术见识的高度。但真的想把这些东西吸收给自己,还是需要个人的拼搏。否则很多东西即使摆在你面前,你也很难看到。你能看到的多数时候只是标题

二、面试题

面试官:好!我看你的简历也没更新,那我们这次聊聊动态代理和反射吧,你了解怎么代理一个类吗?

谢飞机:这个我知道,使用 JDK 自带的类Proxy可以代理一个类,也可以使用 CGLIB 代理。

面试官:嗯,那这两个代理有什么区别呢?

谢飞机:好像一个是 JDK 的需要有接口,CGLIB 的不需要。

面试官:为什么呢?

谢飞机:为什么?这...

面试官:那你自己开发时,用代理做什么业务吗?

谢飞机:... 好像也没有!

飞机只能溜溜的回家了,技术深度不足,也没有实际应用过,还需要很多补全的内容!

三、五种类代理的方式

不出意外,你可能只知道两种类代理的方式。一种是 JDK 自带的,另外一种是 CGLIB。

我们先定义出一个接口和相应的实现类,方便后续使用代理类在方法中添加输出信息。

定义接口

public interface IUserApi {

    String queryUserInfo();

}

实现接口

public class UserApi implements IUserApi {

    public String queryUserInfo() {
        return "小傅哥,公众号:bugstack虫洞栈 | 沉淀、分享、成长,让自己和他人都能有所收获!";
    }

}

好!接下来我们就给这个类方法使用代理加入一行额外输出的信息。

0. 先补充一点反射的知识

@Test
public void test_reflect() throws Exception {
    Class<UserApi> clazz = UserApi.class;
    Method queryUserInfo = clazz.getMethod("queryUserInfo");
    Object invoke = queryUserInfo.invoke(clazz.newInstance());
    System.out.println(invoke);
}


  • 指数:⭐⭐

  • 点评:有代理地方几乎就会有反射,他们是一套互相配合使用的功能类。在反射中可以调用方法、获取属性、拿到注解等相关内容。这些都可以与接下来的类代理组合使用,完成各种框架中的技术场景。

1. JDK 代理方式


  • 指数:⭐⭐

  • 场景:中间件开发、设计模式中代理模式和装饰器模式应用

  • 点评:这种 JDK 自带的类代理方式是非常常用的一种,也是非常简单的一种。基本会在一些中间件代码里看到例如:数据库路由组件、Redis 组件等,同时我们也可以使用这样的方式应用到设计模式中。

2. CGLIB 代理方式


  • 指数:⭐⭐⭐

  • 场景:Spring、AOP 切面、鉴权服务、中间件开发、RPC 框架等

  • 点评:CGLIB 不同于 JDK,它的底层使用 ASM 字节码框架在类中修改指令码实现代理,所以这种代理方式也就不需要像 JDK 那样需要接口才能代理。同时得益于字节码框架的使用,所以这种代理方式也会比使用 JDK 代理的方式快 1.5~2.0 倍。

3. ASM 代理方式


  • 指数:⭐⭐⭐⭐⭐

  • 场景:全链路监控、破解工具包、CGLIB、Spring 获取类元数据等

  • 点评:这种代理就是使用字节码编程的方式进行处理,它的实现方式相对复杂,而且需要了解 Java 虚拟机规范相关的知识。因为你的每一步代理操作,都是在操作字节码指令,例如:Opcodes.GETSTATIC、Opcodes.INVOKEVIRTUAL,除了这些还有小 200 个常用的指令。但这种最接近底层的方式,也是最快的方式。所以在一些使用字节码插装的全链路监控中,会非常常见。

4. Byte-Buddy 代理方式


  • 指数:⭐⭐⭐⭐

  • 场景:AOP 切面、类代理、组件、监控、日志

  • 点评:Byte Buddy 也是一个字节码操作的类库,但 Byte Buddy 的使用方式更加简单。无需理解字节码指令,即可使用简单的 API 就能很容易操作字节码,控制类和方法。比起 JDK 动态代理、cglib,Byte Buddy 在性能上具有一定的优势。另外,2015 年 10 月,Byte Buddy 被 Oracle 授予了 Duke's Choice 大奖。该奖项对 Byte Buddy 的“ Java 技术方面的巨大创新 ”表示赞赏。

5. Javassist 代理方式

public class JavassistProxy extends ClassLoader {

    public static <T> T getProxy(Class clazz) throws Exception {

        ClassPool pool = ClassPool.getDefault();
        // 获取类
        CtClass ctClass = pool.get(clazz.getName());
        // 获取方法
        CtMethod ctMethod = ctClass.getDeclaredMethod("queryUserInfo");
        // 方法前加强
        ctMethod.insertBefore("{System.out.println(\"" + ctMethod.getName() + " 你被代理了,By Javassist\");}");

        byte[] bytes = ctClass.toBytecode();

        return (T) new JavassistProxy().defineClass(clazz.getName(), bytes, 0, bytes.length).newInstance();
    }

}

@Test
public void test_JavassistProxy() throws Exception {
    IUserApi userApi = JavassistProxy.getProxy(UserApi.class)
    String invoke = userApi.queryUserInfo();
    logger.info("测试结果:{}", invoke);
}

/**
 * 测试结果:
 * 
 * queryUserInfo 你被代理了,By Javassist
 * 20:23:39.139 [main] INFO  org.itstack.interview.test.ApiTest - 测试结果:小傅哥,公众号:bugstack虫洞栈 | 沉淀、分享、成长,让自己和他人都能有所收获!
 *
 * Process finished with exit code 0
 */


  • 指数:⭐⭐⭐⭐

  • 场景:全链路监控、类代理、AOP

  • 点评:Javassist 是一个使用非常广的字节码插装框架,几乎一大部分非入侵的全链路监控都是会选择使用这个框架。因为它不想 ASM 那样操作字节码导致风险,同时它的功能也非常齐全。另外,这个框架即可使用它所提供的方式直接编写插装代码,也可以使用字节码指令进行控制生成代码,所以综合来看也是一个非常不错的字节码框架。

四、总结

  • 代理的实际目的就是通过一些技术手段,替换掉原有的实现类或者给原有的实现类注入新的字节码指令。而这些技术最终都会用到一些框架应用、中间件开发以及类似非入侵的全链路监控中。

  • 一个技术栈深度的学习能让你透彻的了解到一些基本的根本原理,通过这样的学习可以解惑掉一些似懂非懂的疑问,也可以通过这样技术的拓展让自己有更好的工作机会和薪资待遇。

  • 这些技术学起来并不会很容易,甚至可能还有一些烧脑。但每一段值得深入学习的技术都能帮助你突破一定阶段的技术瓶颈。

Java堆与栈深度解析:从OOM到StackOverflow的实战定位指南 堆与栈是JVM内存管理的两大核心区域,分别承担对象实例存储与方法调用生命周期管理。其本质差异在于共享性与独占性、GC回收与自动释放、对象实体与引用变量的物理分离。理解这一底层原理,是诊断java.lang.OutOfMemoryError和java.lang.StackOverflowError的关键前提。技术价值体现在性能调优、故障归因与资源合理分配——例如堆溢出多源于对象泄漏或大对象晋升,而栈溢出则指向递归失控或线程栈配置失当。典型应用场景覆盖电商大促压测、微服务内存治理、IoT端Java Agent开 阅读详情

相关推荐

java jvm知识点

✅JVM = 内存 + GC + 类加载 + 调优。

a378113472的博客 179

面经手册 · 第13篇《除了JDK、CGLIB,还有3种类代理方式?面试又卡住!》

作者:小傅哥 博客:https://bugstack.cn 沉淀、分享、成长,让自己和他人都能有所收获!???? 一、前言 编程学习,先铺宽度还是挖深度? 其实技术宽度与技术深度是相辅相成的,你能了解多少技术是和你对一个技术的理解深度有关,而你能对一个技术探究的多深又是需要你有一定的广度认知。否则如果只去了解皮毛或者死磕一段代码,收获都不一定有多大,或者要付出很大的成本。 技术瓶颈,与年龄相关还是大厂? 亲身当过面试官很久,也面试过很多人。有时候不一定年龄很大就技术好,也不一定刚工作2年左右就不行。往往

bugstack虫洞栈 2168

探针技术-JavaAgent 和字节码增强技术-Byte Buddy

Java Agent这个技术对大多数人来说都比较陌生,但是大家都都多多少少接触过一些,实际上我们平时用过的很多工具都是基于java Agent来实现的,例如:热部署工具JRebel,springboot的热部署插件,各种线上诊断工具(btrace, greys),阿里开源的arthas等等。其实java Agent在JDK1.5以后,我们可以使用agent技术构建一个独立于应用程序的代理程序(即Agent),用来协助监测、运行甚至替换其他JVM上的程序。

wr_java的博客 532

Java面试必看!Spring核心

Spring核心面试题摘要 本文总结了2025年大厂面试中Spring框架最常问的20个核心问题,包含标准答案和加分技巧。重点包括:IoC容器启动流程(12步refresh)、Bean生命周期、三级缓存解决循环依赖原理、事务失效场景、AOP代理机制、Spring Boot自动装配原理等。特别针对高频考点如@Transactional失效原因、同类方法调用事务不生效、设计模式应用等提供深入解析。每个问题都提供最简回答和扩展知识点,帮助面试者快速掌握核心要点,适合准备阿里P6、字节2-2等高级岗位的候选人参考。

likuolei的博客 853

码农会锁,synchronized 对象头结构 (mark-word、Klass Pointer)、指针压缩、锁竞争,源码解毒、深度分析!

感觉什么都不会,从哪开始呀!这是最近我总能被问到的问题,也确实是。一个初入编程职场的新人,或是一个想重新努力学习的老司机,这也不会,那也不会,总会犯愁从哪开始。讲道理,毕竟 Java 涉及的知识太多了,要学应该是学会学习的能力,而不是去背题、背答案,拾人牙慧是不会有太多收益的。学习的过程要找对方法,遇到问题时最好能自己想想,你有哪些方式学会这些知识。是不感觉即使让你去百度搜,你都不知道应该拿哪个关键字搜!只能拿着问题直接找人问,这样缺少思考,缺少大脑撞南墙的过程,其实最后也很难学会。

wr_java的博客 413

从 Mybatis 源码中,学习到的 10 种设计模式

总有不少研发伙伴问小傅哥:“为什么学设计模式、看框架源码、补技术知识,就一个普通的业务项目,会造飞机不也是天天写 CRUD 吗?你说的没错,但你天天写 CRUD,你觉得烦不?慌不?是不是既担心自己没有得到技术成长,也害怕将来没法用这些都是 CRUD 的项目去参加;述职、晋升、答辩,甚至可能要被迫面试时,自己手里一点干货也没有的情况。所以你/我作为一个小镇卷码家,当然要扩充自己的知识储备,否则架构,架构思维不懂设计,设计模式不会源码、源码学习不深,最后就用一堆 CRUD 写简历吗?

wr_java的博客 240

这是我见过最有用的java面试题,面试了无数公司总结的。

一、Java基础部分 1. Java面向对象的三个特征 封装:对象只需要选择性的对外公开一些属性和行为。 继承:子对象可以继承父对象的属性和行为,并且可以在其之上进行修改以适合更特殊的场景需求。 多态:允许不同类的对象对同一消息做出响应。 2. Java中基本的数据类型有哪些 以及他们的占用字节 3. int和Integer的区别 int是Java中的原始类型,Integer是Java为int提供的封装类,他们有不同的特征和用法,包括大小、速度、默认值。 4. String、Stri...

m0_58336848的博客 220

雪莉果的Axure原型工厂vol.006 中继器应用(二)——多选穿梭框

如果你用axure有一段时间了,那么你很快就能琢磨出一个大体设计流程,比如加一个中间过度的元件,来储存需要穿梭的内容;但是如果你再想一想,就会发现,这样做,似乎只能解决单选,那么多选需要怎么操作呢? 这时候,中继器就会告诉你,我还能做的更多。

weixin_43829792的博客 976

面试问题总结

文章目录1.abstract和interface异同2.构造方法3.final关键字作用4.重写和重载的区别4.1重载4.2重写5.this关键字6.封装的作用,实现封装的步骤7.封装中的getter和setter方法分别作用7.1setter方法7.2getter方法8.Mybatis-plus与Spring Data JPA9.装箱和拆箱9.1装箱9.2拆箱10.ArrayList底层原理11.反射12.Filter13.$与#的区别14.js中的DOM对象与jQuery中的DOM区别15.四大元注解1

肥猫不是猫 1110

贪婪投影三角化算法

贪婪投影三角化算法是一种利用点云数据进行曲面重建的方法。其基本思想是通过迭代地连接点云中的邻近点来生成三角形,从而构建出整个表面。这种方法被称为“贪婪”,因为它在每一步仅考虑当前最优的策略,而不考虑全局的最优性。

twnkie的博客 1567

JVM 常见面试问答:深入底层原理

本文为Java后端开发、架构师及中间件开发岗位面试提供全面的JVM知识体系,涵盖内存模型、对象创建、类加载、垃圾回收、JIT编译、性能调优等核心内容。内容分为14个章节,从基础概念到高级场景实战,包括: JVM内存模型:程序计数器、虚拟机栈、堆、方法区等运行时数据区详解。 对象创建与内存分配:对象实例化过程、TLAB机制、逃逸分析等。 类加载机制:双亲委派模型、类加载阶段、自定义类加载器。 垃圾回收:分代回收算法、CMS/G1/ZGC等回收器对比与原理。

weixin_72753562的博客 1017

干货|C#上位机与西门子PLC的Profinet协议通信开发(工控机版)

文章摘要: Profinet是西门子主导的工业以太网协议,支持实时通信(RT/IRT),广泛应用于PLC与工控机通信。本文聚焦C#上位机通过S7协议(集成于Profinet接口)高效访问西门子PLC(S7-1200/1500等),提供完整开发方案: 协议基础:解析Profinet三层架构(TCP/IP层、实时层、应用层),对比S7协议优势(开发便捷、跨平台); 开发实践:基于S7NetPlus/Snap7库实现数据读写,涵盖DB块、I/O点等地址规则; 环境配置:工控机(Windows/Linux/统信UO

这里只有干货:从 Modbus 通信到 MES 对接,从 YOLO 训练到工控机部署,带你搞定工业软件开发全流程。 1213

分布式定时任务调度框架选型

背景 业务场景 定期执行任务:如每天0点做资源稽查; 需求和痛点 集群部署服务时,如何确保任务不被重复执行?---最急迫 如何监控、告警等; 高可用、无单点故障; 优秀的并行处理能力、分片能力; 自研 or 开源 任何工具的使用都要结合自身的业务场景,脱落业务场景谈技术选型就是耍流氓。 考虑私有云场景业务量一般,高并发场景很少遇到,同一时间也不会有超大量定时任务同时需要执行,所以考虑自研也未尝不可。 目前自研最急需解决的问题并不是高并发,而是如何避免任务被重复执行; 场景就变成了: 多个应

wr_java的博客 1941

10、皮肤 - 神经记录实验全流程解析

本博客详细介绍了皮肤-神经记录实验的全流程,涵盖实验所需的材料准备和具体操作方法。内容包括细胞外记录设备的设置、合成间质液(SIF)的制备与调节、小鼠后爪皮肤和胫神经的手术解剖步骤、清洁解剖后的记录准备、纤维分离与感受野识别方法,以及神经纤维机械响应的测量。该实验为研究神经纤维的活动与功能提供了重要的技术支持,适用于神经科学研究领域。

backprop5master的博客 185

【linux】解决报错:Network error: Connection refused

【linux】解决报错:Network error: Connection refused

weixin_44502754的博客 4936

低延迟的超时中心实现方式

在不少产品中都存在生命周期相关的设计,时间节点到了以后须要作对应的事情。超时中心(TimeOutCenter,TOC)负责存储和调度生命周期节点上面的超时任务,当超时任务设置的超时时间到期后,超时中心须要当即调度处理这些超时任务。对于一些须要低延迟的超时场景,超时中心调度延迟会给产品带来不可估量的影响。sql做者 | 默达来源 | 阿里技术公众号数据库。

wr_java的博客 1044

(王道408考研操作系统)第一章计算机系统概述-第三节2:中断和异常

注意!!! 本节内容和《计算机组成原理》中的中断有重合,如果需要详细了解的话可以点击链接跳转:(计算机组成原理)第七章输入和输出系统-第四节2:I/O方式之程序中断方式 为了内容完整,部分内容会借用上述文章 文章目录一:中断和异常的基本概念(1)中断和异常的定义 一:中断和异常的基本概念 在操作系统引入核心态和用户态这两种工作状态后,就需要考虑两种状态之间是如何切换的。 操作系统内核工作在核心态,用户程序工作在用户态,系统不允许用户程序使用核心态的功能,但是用户程序为了完成某些操作又必须使用这些功能

快乐江湖的博客 3530

当面试官问我ArrayList和LinkedList哪个更占空间时,我这么答让他眼前一亮

前言今天介绍一下Java的两个集合类,ArrayList和LinkedList,这两个集合的知识点几乎可以说面试必问的。对于这两个集合类,相信大家都不陌生,ArrayList可以说是日常开发中用的最多的工具类了,也是面试中几乎必问的,LinkedList可能用的少点,但大多数的面试也会有所涉及,尤其是关于这两者的比较可以说是家常便饭,所以,无论从使用上还是在面试的准备上,对于这两个类的知识点我们都要有足够的了解。

wr_java的博客 479

Unreal从入门到精通之如何绘制用于VR的3DUI交互的手柄射线

之前我写过一篇文章《Unreal5从入门到精通之如何在VR中使用3DUI》,其中讲到使用WidgetComponent和3DUI进行交互。然而,这种方法存在一个问题:为了让射线可视化,我们需要勾选“调试/显示调试”,这样才能在手柄上看到代表其指向的一条红色射线。但最关键的问题是,这个显示调试信息的功能只属于Debug调试模式,也就是说,一旦将项目正式打包发布,这条射线辅助线就没有了。如下图所示:我就觉得Unreal官方真的是有点过分了,这不应该是最基本的功能么?难道我们VR要在Debug模式下发布?

TxNet 1612

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

java队列——queue详细分析

Queue: 基本上,一个队列就是一个先入先出(FIFO)的数据结构 Queue接口与List、Set同一级别,都是继承了Collection接口。LinkedList实现了Deque接 口。 Queue的实现 1、没有实现的阻塞接口的LinkedList: 实现了java.util.Queue接口和java.util.AbstractQueue接口 内置的不阻塞队列:PriorityQueue和ConcurrentLinkedQueue   PriorityQueue 和 Concurrent...

wr_java的博客 401

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

延迟任务场景,该如何提高吞吐量和时效性!

不卷了,能用就行!哈哈哈,说好的不卷了,能凑活用就行了。但每次接到新需求时都手痒,想结合着上一次的架构设计和落地经验,在这一次需求上在迭代更新,或者找到完全颠覆之前的更优方案。卷完代码的那一刻总是神清气爽其实大部分喜欢写代码的一类纯粹码农,都是比较卷的,就比如一个需求在实现上是能用大概是P5、如果这个做出来的功能不只是能用还非常好用是P6、除了好用还凝练共性需求开发成通用的组件服务是P7。

wr_java的博客 309

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

图文并茂,带你深入了解AQS的源码

如果你想深入研究Java并发的话,那么AQS一定是绕不开的一块知识点,Java并发包很多的同步工具类底层都是基于AQS来实现的,比如我们工作中经常用的Lock工具ReentrantLock、栅栏CountDownLatch、信号量Semaphore等,而且关于AQS的知识点也是面试中经常考察的内容,所以,无论是为了更好的使用还是为了应付面试,深入学习AQS都很有必要。CAS。

wr_java的博客 302

深入剖析java并发之阻塞队列LinkedBlockingQueue与ArrayBlockingQueue

阻塞队列概要 阻塞队列与我们平常接触的普通队列(LinkedList或ArrayList等)的最大不同点,在于阻塞队列支出阻塞添加和阻塞删除方法。 阻塞添加 所谓的阻塞添加是指当阻塞队列元素已满时,队列会阻塞加入元素的线程,直队列元素不满时才重新唤醒线程执行元素加入操作。 阻塞删除 阻塞删除是指在队列元素为空时,删除队列元素的线程将被阻塞,直到队列不为空再执行删除操作(一般都会返回被删除的元素) 由于Java中的阻塞队列接口BlockingQueue继承自Queue接口,因此先来看

wr_java的博客 265

网易二面:CPU狂飙900%,该怎么处理?

一位小伙伴面试了 网易,遇到了一个 性能类的面试题:CPU飙升900%,该怎么处理?可惜的是,以上的问题,这个小伙没有回答理想。最终,导致他网易之路,终止在二面,非常可惜。

wr_java的博客 192
上一篇: ZooKeeper 避坑实践:如何调优 jute.maxbuffer
下一篇: 探针技术-JavaAgent 和字节码增强技术-Byte Buddy
wrr-cat
博客等级 码龄9年 128粉丝 302原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值