首页/新闻资讯/正文详情

ASM 自定义属性(Attribute)机制实战:用 `Attribute` 子类扩展类文件格式,CodeGuide 附录 A.3 深度解析

发布时间:2026/9/24 6:25:58 来源:云帆数科 栏目:资讯中心
ASM 自定义属性(Attribute)机制实战:用 `Attribute` 子类扩展类文件格式,CodeGuide 附录 A.3 深度解析
文档教程后端【免费下载链接】CodeGuide:books: 本代码库是作者小傅哥多年从事一线互联网 Java 开发的学习历程技术汇总旨在为大家提供一个清晰详细的学习教程侧重点更倾向编写Java核心内容。如果本仓库能为您提供帮助请给予支持(关注、点赞、分享)项目地址https://gitcode.com/gh_mirrors/code/CodeGuide点击查看免费下载本篇文章基于 CodeGuide 仓库中 ASM 文档的 A.3 属性 一章系统讲解 JVM 类文件格式中属性Attribute这一可扩展机制的底层原理以及 ASM 中Attribute类的完整用法。读完本文你将掌握类、字段、方法三级属性在 ASM 事件模型中的访问入口ClassReader对标准属性与非标准属性的默认处理策略以及如何通过编写Attribute子类实现自定义属性的解码与重新编码并正确注册到ClassReader.accept中。属性机制类文件格式留给扩展的后门在 2.1 结构 一章中可以看到一个已编译类的整体结构由多个部分组成类描述修饰符、名字、超类、接口、常量池、源文件名、封装的类引用、注释、内部类以及类中声明的每个字段和方法各自对应的部分。其中类、字段、方法这三层结构上都留有一个带星号的属性*槽位星号表示零个或多个已编译类的整体结构*表示零个或多个 ├── 修饰符、名字、超类、接口 ├── 常量池数值、字符串和类型常量 ├── 源文件名可选 ├── 封装的类引用 ├── 注释* ├── 属性* ├── 内部类* ├── 字段* 修饰符、名字、类型 │ 注释* │ 属性* └── 方法* 修饰符、名字、返回类型与参数类型 注释* 属性* 编译后的代码属性机制正是 JVM 规范为类文件格式留出的可扩展后门可以将任意属性关联到类、字段和方法。在引入新特性时这种可扩展机制对于扩展类文件格式非常有用。例如它已经被用于扩展这一格式以支持注释Annotations、泛型Signature 属性、栈映射帧StackMapTable等 JVM 新特性——这些特性都是以标准属性的形式注入类文件的。这一机制还可由用户使用而不只是由 JVM 规范制定者使用。不过自从在 Java 5 中引入注释之后注释的使用就比属性容易得多——注释有语言级语法支持可以被编译器、工具链和反射 API 统一处理。因此如果你真的需要使用自己的属性或者必须管理由别人定义的非标准属性就需要在 ASM 中用Attribute类来完成。默认行为ClassReader 如何对待未知属性默认情况下ClassReader类会为它找到的每个标准属性创建一个Attribute实例并以这个实例为参数调用visitAttribute方法。至于调用的是ClassVisitor、FieldVisitor还是MethodVisitor的该方法则取决于属性出现的上下文——也就是说ASM 的事件模型在类、字段、方法三个层级上分别暴露了属性访问入口详见 2.2 接口和组件 与 3.2 接口和组件 中的ClassVisitor.visitAttribute、FieldVisitor.visitAttribute、MethodVisitor.visitAttribute方法。对于非标准未知属性Attribute实例中保存的是这个属性的原始内容其形式为一个私有的字节数组。ClassWriter在访问这种未知属性时会将这个原始字节数组原样复制到它构建的新类中。默认复制行为为何依赖 2.2.4 节的优化这里有一个非常关键的隐含前提这一默认行为只有在使用 2.2.4 节 介绍的优化时才是安全的。所谓 2.2.4 节的优化指的是ClassReader与ClassWriter互相持有对方引用时的常量池复制优化ClassReader cr new ClassReader(b1); ClassWriter cw new ClassWriter(cr, 0); ChangeVersionAdapter ca new ChangeVersionAdapter(cw); cr.accept(ca, 0); byte[] b2 cw.toByteArray();当ClassReader检测到它返回的MethodVisitor来自一个ClassWriter时意味着该方法的内容不会被转换ClassReader就不会去分析方法内容、生成相应事件而是直接把ClassWriter中表示该方法的字节数组复制过去。原始属性内容本质上是一段与常量池索引交织的字节序列——属性内部往往通过索引引用常量池中的 UTF8 字符串等常量。如果不使用复制常量池优化ClassWriter会构建一个全新的常量池原来属性字节中记录的常量池索引就会失效原内容可能会与类编写器创建的新常量池不一致从而导致类文件被损坏。而使用该优化时原类中定义的所有常量都会被复制到转换后的类中属性字节内的索引得以原样保留。所以这一优化除了提高性能外还是保证未知属性原样复制安全性的另一重要原因。关于该优化的性能收益A.5 性能 一章给出的基准数据显示复制常量池优化可提速 15%20%。默认行为的局限性内容不透明默认情况下非标准属性会以它在已转换类中的形式被复制它的内容对 ASM 和用户来说是完全不透明的——ASM 不做任何解析你也无法读取或修改其中的业务信息。如果只是路过某个类、不希望破坏其自定义属性这个默认行为是够用的但如果你需要访问属性内容就必须主动介入定义一个Attribute子类实现对该属性原始内容的解码read与重新编码write在ClassReader.accept方法中传送这个类的一个原型实例使ClassReader可以解码这一类型的属性。实战编写 CommentAttribute 自定义属性下面用文档中的完整示例来演示。假设我们要实现一个设想的注释特性它的原始内容是一个short值引用存储在常量池中的一个 UTF8 字符串。对应的Attribute子类如下class CommentAttribute extends Attribute { private String comment; public CommentAttribute(final String comment) { super(Comment); this.comment comment; } public String getComment() { return comment; } Override public boolean isUnknown() { return false; } Override protected Attribute read(ClassReader cr, int off, int len, char[] buf, int codeOff, Label[] labels) { return new CommentAttribute(cr.readUTF8(off, buf)); } Override protected ByteVector write(ClassWriter cw, byte[] code, int len, int maxStack, int maxLocals) { return new ByteVector().putShort(cw.newUTF8(comment)); } }逐段剖析这个类构造器与属性名super(Comment)将属性名固定为Comment。属性名在类文件格式中是一个常量池索引指向 UTF8 字符串JVM 用它来识别属性类型自定义属性名应避免与已登记的标准属性名冲突。isUnknown()返回false这是向 ASM 表明本类不是未知属性而是可被 ASM 识别、可解码的自定义属性类型。这也是继承Attribute时必须重写的方法。read方法对这一类型的属性的原始内容进行解码。参数中的off是属性值在ClassReader所读字节数组中的偏移量buf是用于读取 UTF8 字符串的临时字符缓冲。这里调用cr.readUTF8(off, buf)直接从偏移处读出 UTF8 字符串——这也印证了原始内容中short值引用常量池 UTF8 字符串的结构设计。write方法执行逆操作即重新编码。cw.newUTF8(comment)在ClassWriter的常量池中创建或复用一个 UTF8 常量并返回其索引new ByteVector().putShort(...)将该索引以short形式写入属性字节数组。注意read方法必须返回一个新的属性实例。ASM 会为每个被识别的该类型属性分别调用一次read并收集返回的新实例因此不能在read中返回this或复用已有实例。注册原型实例仅仅定义子类还不够。ClassReader需要知道遇到名为Comment的属性时应该用哪个类去解码。为此必须在调用accept时传入该类型的一个原型实例ClassReader cr ...; ClassVisitor cv ...; cr.accept(cv, new Attribute[] { new CommentAttribute() }, 0);accept的第二个参数是一个Attribute数组里面放的是原型实例原型中的字段值无关紧要解码时会被read生成的新实例覆盖。accept的第三个参数是int类型的解析标志此处为 0表示默认解析行为。完成注册后类文件中的每一个Comment属性都将被识别并为它们中的每一个都创建一个CommentAttribute实例——这些实例携带解码后的comment值可以在你的ClassVisitor的visitAttribute回调中直接使用。而其他未被识别的属性仍会用普通的Attribute实例表示继续走原样复制的默认路径。属性与注释什么时候该用自定义属性JVM 类文件中的注释机制 可以看作属性的进化形态类、字段、方法和方法参数注释只要保留策略不是RetentionPolicy.SOURCE就会被存储在编译后的类中ASM为其提供了独立的AnnotationVisitor访问器体系visitAnnotation、visitEnum、visitArray等ASMifier 甚至可以直接生成构造注释类的 ASM 代码。两者对比实践中的选择标准可以归纳为能用注释解决的问题优先用注释注释有源代码语法、编译器检查、javac与反射 API 的统一支持AnnotationVisitor提供类型安全的嵌套结构访问开发成本远低于属性。必须处理非标准属性时才使用Attribute典型场景包括——读取第三方库生成的、带有私有属性的类文件实现 JVM 规范之外、未被 ASM 内置识别的标准属性如 ASM 自身尚未支持的较新规范属性或者你确实需要向类文件中写入不透明的二进制附加信息。三处访问入口类、字段、方法的 visitAttribute由于属性可以挂在类、字段、方法三个层级上ASM 在三个访问器抽象类中分别定义了visitAttribute(Attribute attr)方法参见 ClassVisitor 与 MethodVisitor 的源码结构访问器方法触发时机ClassVisitorvisitAttribute(Attribute attr)在visit之后、visitInnerClass/visitField/visitMethod之前FieldVisitorvisitAttribute(Attribute attr)在字段的visitAnnotation之后、visitEnd之前MethodVisitorvisitAttribute(Attribute attr)在方法的注释与参数注释之后、visitCode之前事件模型的调用顺序约束见 2.2 接口和组件visit visitSource? visitOuterClass? ( visitAnnotation | visitAttribute )* ( visitInnerClass | visitField | visitMethod )* visitEnd方法层级的顺序约束见 3.2 接口和组件visitAnnotationDefault? ( visitAnnotation | visitParameterAnnotation | visitAttribute )* ( visitCode ... visitMaxs )? visitEnd因此在编写类适配器时若要读取自定义属性并据此调整转换逻辑需要重写对应层级的visitAttribute若要新增属性则需要遵循上述调用顺序将visitAttribute调用插入到正确的时间点。使用工具验证自定义属性在开发Attribute子类时2.3 工具 一章提供的调试工具同样适用TraceClassVisitor生成所访问类的文本表示。用它替代或包裹ClassWriter可以直观地看到自定义属性是否被正确解析、read/write往返后内容是否一致。CheckClassAdapter验证对visitAttribute等方法的调用顺序与参数是否合法在属性接入事件链出现顺序错误时提前抛出异常避免产出被 JVM 验证器拒绝的类文件。ASMifier如果你想知道如何用 ASM 生成某个带属性的类先写 Java 源码、用javac编译再用 ASMifier 访问编译产物即可得到对应的 ASM 生成代码其中就包括visitAttribute的调用写法。小结围绕 A.3 属性本文梳理了完整的技术闭环属性是 JVM 类文件格式官方的可扩展机制可挂在类、字段、方法三层ClassReader默认把未知属性封装为不透明的Attribute实例通过visitAttribute交给访问链ClassWriter原样复制其原始字节原样复制只有在启用 2.2.4 节 的常量池复制优化时才是安全的否则新常量池会让属性内索引失效、损坏类文件要读写自定义属性需定义Attribute子类并重写read/write/isUnknown再通过ClassReader.accept(cv, new Attribute[]{...}, 0)注册原型实例Java 5 注释机制出现后能用注释解决的场景优先用注释自定义属性只用于处理非标准属性或二进制附加信息的场景。CodeGuide 仓库中 asm-document 目录还包含完整的属性、注释、方法、后向兼容等章节如 A.4 规则、4.2 注释、A.5 性能结合 ASM 字节码编程实战系列 中的 JavaAgent 与字节码插桩案例Javassist 篇、Byte-buddy 篇、ASM 篇阅读可以形成从原理到落地的完整知识链路。赞分享文档教程后端【免费下载链接】CodeGuide:books: 本代码库是作者小傅哥多年从事一线互联网 Java 开发的学习历程技术汇总旨在为大家提供一个清晰详细的学习教程侧重点更倾向编写Java核心内容。如果本仓库能为您提供帮助请给予支持(关注、点赞、分享)项目地址https://gitcode.com/gh_mirrors/code/CodeGuide点击查看免费下载相关推荐深度解析 NumPy 文档构建autosummary attribute 模板与类属性文档页生成机制深度解析 NumPy 文档构建autosummary attribute 模板与类属性文档页生成机制 导读 Sphinx 的 autosummary 扩展是科学计算数据分析Cherry Studio 完整教程3 步接入 300 AI 模型桌面端一站式搞定Cherry Studio 完整教程3 步接入 300 AI 模型桌面端一站式搞定 你是不是也遇到过这种场面OpenAI 的 key 躺在浏览器书签里AI 应用大模型桌面应用本地部署RAGPHPStan 错误标识符解析attribute.trait——Trait 不能用作属性Attribute类PHPStan 错误标识符解析attribute.trait——Trait 不能用作属性Attribute类 本文以 PHPStan 官方错误标识符文档开发工具代码质量静态分析创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关推荐

BosonNetSim实战:VLAN划分与路由协议配置从入门到排错
BosonNetSim实战:VLAN划分与路由协议配置从入门到排错

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/24 6:25:51

空气动力学PDF资料实战指南:从无量纲数到CFD仿真落地
空气动力学PDF资料实战指南:从无量纲数到CFD仿真落地

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/24 6:25:27

DeepSeek应用一体机私有化部署:硬件选型与场景落地指南
DeepSeek应用一体机私有化部署:硬件选型与场景落地指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/24 6:25:21

SD-WAN选型全维度解析:从全球覆盖到交付保障
SD-WAN选型全维度解析:从全球覆盖到交付保障

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/24 7:04:18

oeasy教h5前端002 nginx_静态网页服务器
oeasy教h5前端002 nginx_静态网页服务器

002 nginx_静态网页服务器 nginx_静态网页服务器 新的开始 上次 我们修改了网页在 已有网页上右键 检查元素可以修改 文字改出 自己的热搜、热榜图片 也能修改让喜欢的事物 霸屏吧!!! 为啥 可以 这样玩呢?🤔 直接浏览 firefox http://localhost/连接… · 2026/9/24 7:04:18

CC-Switch下载安装教程-2026最新DeepSeek渠道配置与Codex本地调用设置完整步骤详解(Windows·Mac·Linux全平台图文)
CC-Switch下载安装教程-2026最新DeepSeek渠道配置与Codex本地调用设置完整步骤详解(Windows·Mac·Linux全平台图文)

文章目录一、CC-Switch配置解决什么问题二、CC-Switch下载安装前要准备什么1. 一个有效的 DeepSeek API Key2. 确认自己的操作系统三、CC-Switch 下载安装包地址四、分平台安装步骤Windows 安装macOS 安装Linux 安装五、配置第一步:新增 API 渠道六、配置第二步&… · 2026/9/24 7:04:12

AD736真有效值转换器外围电路设计与调试实战
AD736真有效值转换器外围电路设计与调试实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/24 7:04:12

小米做了一个“架构最平淡“的模型,然后拿了开源第一:这里面藏着今年最重要的一课
小米做了一个“架构最平淡“的模型,然后拿了开源第一:这里面藏着今年最重要的一课

摘要2026 年 9 月 21 日,小米 MiMo 团队发布了 MiMo-V2.6 系列,旗舰款 MiMo-V2.6-Pro 以 46 分登上 Artificial Analysis 智能指数榜首——这是开源权重模型的历史最高分。而它的前代只有 26 分。有意思的地方在于:它的架构非常"平淡&qu… · 2026/9/24 7:04:11

稳定无广告!电脑必备免费办公套件
稳定无广告!电脑必备免费办公套件

给大家安利一款干净又好用的办公软件——LibreOffice。 很多人不知道这款开源神器,它可以直接平替Office和WPS,永久免费使用,没有任何弹窗广告,也不需要繁琐的激活步骤。 深度兼容所有办公文档格式,平时的Word、Ex… · 2026/9/24 7:03:59

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程
基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

简介:这是一套面向计算机、人工智能、自动化等专业学生与教师的毕业设计级项目资源,围绕YOLOv8实现渔船作业监控系统,可用于毕设、课程设计、大作业或项目立项演示。压缩包共97个文件,约24.21MB,以70个Python源码文件为… · 2026/9/24 0:00:13

1D-CNN时间序列建模实战:从Conv1d原理到工业落地
1D-CNN时间序列建模实战:从Conv1d原理到工业落地

简介:面向时间序列数据建模的一维卷积神经网络完整实现,适合深度学习入门者及需要快速验证时序模型的研究者,能够从音频、文本、传感器或股价等序列中挖掘局部特征与时间依赖。压缩包体积很小,只有3KB,内含3个Python脚… · 2026/9/24 0:00:26

柔软的L:汉语语流中被忽视的舌肌张力控制
柔软的L:汉语语流中被忽视的舌肌张力控制

1. 这个“L”不是字母表里的L,而是舌尖上的L最近在几个方言群和语音教学社群里,反复看到有人发一句:“也说字母L:柔软的长舌”。初看以为是英语发音课笔记,点开才发现全是方言爱好者、播音系学生、语言康复师甚至戏曲演… · 2026/9/24 0:00:44

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码