从利用Arthas排查线上Fastjson问题到Java动态字节码技术(下)
上一篇从Arthas的源码引出了Java动态字节码技术,那么这一篇就从几种Java字节码技术出发,看看Arthas是如何通过动态字节码技术做到无侵入的源码增强;
Java大部分情况下都是解释执行的,也就是解释.class
文件,所以如果我们想对原代码进行增强的话,直接接的手段便是从源文件.java
入手,使用静态代理、动态代理、装饰器等设计模式进行功能增强。但很多时候我们作为第三方,没有机会、不方便拿到源码时,这条路就走不通了;此时如果还是想继续其进行功能增强的话,那么只剩一条路了,就是直接对.class
文件下手。
但对于二进制的.class
文件,还有多少人能分清魔数、标识符、各种区域表示的内容呢?所以直接操作字节码也似乎不太现实,那有没有什么抓手呢?
ASM
ASM 是 assembly 汇编 的简称,所以它更偏底层、更贴近底层字节码,所以也就更难使用、更晦涩。
整体思路上是通过Reader将.class
文件读取后,通过一系列API来增强源码,之后通过Writer生成新的.class
文件。
个人不太建议使用这种方式,学习和使用成本都比较高,不直观,太晦涩。
Javaassist
相比于ASM,理解起来不要太简单,不需要了解底层虚拟机指令和晦涩的API,只需要掌握四个核心类即可 ClassPool、CtClass、CtMethod、CtField:
CtClass(compile-time class):编译时 - 类对象,Java中万物皆对象,CtClass是Class在编译时的对象,可以通过类的全限定名来获取到这个CtClass;
与编译时(compile)类对象相似的,我们平时用反射获取到的是运行时(Runtime)类对象;
ClassPool:可以把它简单的理解为一个HashMap,类的全限定名作为key,可以从中获取到CtClass
CtMethod & CtField:类中的方法和属性
利用以上四个类,可以快速实现对源码的增强,比如当你想实现AOP,在原方法执行前和执行后添加功能的时候,只需要先通过ClassPool获取到CtClass,再获取到CtMethod后,就可以调用该方法的insertBefore
和insertAfter
,插入具体代码块了。
Instrument
Instrument是JVM提供的一个对已加载的类进行修改的接口(Interface),打开java.lang.Instrumentation
可以看到清晰的说明。
首先Instrument
是基于 Java Agent
技术的,也就是上一篇中提到的,用 agentmain
和 premain
attache java进程生成的 agent,也就是为 Instrument 在运行的进程中开了一个后门。
在instrument
接口中最重要的方法是 通过ClassFileTransformer
对原class进行 retransform
;
在ClassFileTransformer
中实现想要对原class的增强,该接口只有一个需要被子类实现的方法,就是transform
方法:
package java.lang.instrument;public interface ClassFileTransformer {byte[]transform( ClassLoader loader,String className,Class<?> classBeingRedefined,ProtectionDomain protectionDomain,byte[] classfileBuffer)throws IllegalClassFormatException;
}
举一个直观的例子,福报厂的ttl threadpool
:
package com.alibaba.ttl.threadpool.agent;public class TtlTransformer implements ClassFileTransformer {//类似于责任链,可以添加一系列的 ClassFileTransformer//值得注意的是,这里使用到了Javassistprivate final List<JavassistTransformlet> transformletList = new ArrayList<JavassistTransformlet>();TtlTransformer(List<? extends JavassistTransformlet> transformletList) {for (JavassistTransformlet transformlet : transformletList) {this.transformletList.add(transformlet);logger.info("[TtlTransformer] add Transformlet " + transformlet.getClass() + " success");}}@Overridepublic final byte[] transform(@Nullable final ClassLoader loader, @Nullable final String classFile, final Class<?> classBeingRedefined,final ProtectionDomain protectionDomain, @NonNull final byte[] classFileBuffer) {final String className = toClassName(classFile);//通过classloader和classname加载了原class对象ClassInfo classInfo = new ClassInfo(className, classFileBuffer, loader);//对原class对象,依次应用 ClassFileTransformer,并返回transform后的新class对象for (JavassistTransformlet transformlet : transformletList) {transformlet.doTransform(classInfo);if (classInfo.isModified()) return classInfo.getCtClass().toBytecode();}}
}
这时就可以回到上一篇中Arthas的核心启动类ArthasBootstrap
==> transformerManager = new TransformerManager(instrumentation);
在TransformerManager中,有三类增强功能:watcher/tarce/其余的归为reTransformers
public TransformerManager(Instrumentation instrumentation) {this.instrumentation = instrumentation;classFileTransformer = new ClassFileTransformer() {@Overridepublic byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {for (ClassFileTransformer classFileTransformer : reTransformers) {byte[] transformResult = classFileTransformer.transform(loader, className, classBeingRedefined,protectionDomain, classfileBuffer);if (transformResult != null) {classfileBuffer = transformResult;}}for (ClassFileTransformer classFileTransformer : watchTransformers) {byte[] transformResult = classFileTransformer.transform(loader, className, classBeingRedefined,protectionDomain, classfileBuffer);if (transformResult != null) {classfileBuffer = transformResult;}}for (ClassFileTransformer classFileTransformer : traceTransformers) {byte[] transformResult = classFileTransformer.transform(loader, className, classBeingRedefined,protectionDomain, classfileBuffer);if (transformResult != null) {classfileBuffer = transformResult;}}return classfileBuffer;}};instrumentation.addTransformer(classFileTransformer, true);
}
以其中的Watcher为例子,看下Arthas底层是如何增强源码的;
//继承java.lang.instrument包中的 ClassFileTransformer 接口
public class Enhancer implements ClassFileTransformer {@Overridepublic byte[] transform(final ClassLoader inClassLoader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) {//底层是ASMClassNode classNode = new ClassNode(Opcodes.ASM9);//首先获取原class对象ClassReader classReader = AsmUtils.toClassNode(classfileBuffer, classNode);classNode = AsmUtils.removeJSRInstructions(classNode);List<MethodNode> matchedMethods = new ArrayList<MethodNode>();for (MethodNode methodNode : classNode.methods) {if (!isIgnore(methodNode, methodNameMatcher)) {matchedMethods.add(methodNode);}}for (MethodNode methodNode : matchedMethods) {if (AsmUtils.isNative(methodNode)) {continue; //过滤调native方法}if(AsmUtils.containsMethodInsnNode(methodNode, Type.getInternalName(SpyAPI.class), "atBeforeInvoke")) {... //对class的方法添加watch功能}}return AsmUtils.toBytes(classNode, inClassLoader, classReader); //返回新的class对象}
}
相关文章:
从利用Arthas排查线上Fastjson问题到Java动态字节码技术(下)
上一篇从Arthas的源码引出了Java动态字节码技术,那么这一篇就从几种Java字节码技术出发,看看Arthas是如何通过动态字节码技术做到无侵入的源码增强; Java大部分情况下都是解释执行的,也就是解释.class文件,所以如果我们…...

Ubuntu中安装Anaconda 如何将 路径导入为全局变量
第一步:将你的anaconda 路径复制下来,在终端输入对应路径。 echo export PATH"/home/你的用户名/anaconda3/bin:$PATH" >> ~/.bashrc 第二步:在终端输入下面命令或者重启系统。 source ~/.bashrc 在对应的anaconda安装目…...

【QT】Qt的随身笔记(持续更新...)
目录 Qt 获取当前电脑桌面的路径Qt 获取当前程序运行路径Qt 创建新的文本文件txt,并写入内容如何向QPlainTextEdit 写入内容QTimerQMessageBox的使用QLatin1StringQLayoutC在c头文件中写#include类的头文件与直接写class加类名有何区别mutable关键字前向声明 QFontQ…...

【LeetCode-简单题】589. N 叉树的前序遍历
文章目录 题目方法一:单循环栈做法方法二:递归 题目 方法一:单循环栈做法 关键在于子节点的入栈顺序,决定了子节点的出栈顺序, 因为是前序遍历 所以压栈顺序先让右边的入栈 依次往左 这样左边的节点会在栈顶 这样下次…...
Linphone3.5.2 ARM RV1109音视频对讲开发记录
Linphone3.5.2 ARM RV1109音视频对讲开发记录 说明 这是一份事后记录,主要记录的几个核心关键点,有可能很多细节没有记上,主要是方便后面自己再找回来! 版本 3.5.2 一些原因选的是这样一个旧的版本! 新的开发最好选新一些的版…...

Unity用相机实现的镜子效果
首先登场 场景中的元素 mirror是镜子,挂着我们的脚本,Quad是一个面片。Camera是用来生成RenderTexture给面片的。里面的test1是我用来调试位置的球。 镜子size是大小,x是-2,为了反转一下贴图 相机直接可以禁用掉,用…...

计算机网络分类
按照覆盖范围分类 (1)个域网:通常覆盖范围在1~10m。 (2)局域网:通常覆盖范围在10m~1km。 (3)城域网:覆盖范围通常在5~50 km 。 &…...

AI AIgents时代 - (三.) AutoGPT和AgentGPT
前两篇讲解了Agent的原理和组件,这节我将给大家介绍两个agent项目,给出它们的工作原理和区别,并教大家亲手尝试使用 Agents🎉 🟢 AutoGPT🤖️ 我们的老朋友,之前文章也专门写过。AutoGPT 是一…...
Jmeter接口自动化和Python接口自动化,如何选择?
选择Jmeter或Python进行接口自动化测试取决于您的具体需求和环境。以下是一些可以考虑的因素: 1. 语言熟悉度:如果您对Java更熟悉,那么Jmeter可能是更好的选择。而如果您的团队或个人对Python更熟悉,那么Python可能是更好的选择。…...

Sqilte3初步教程
文章目录 安装创建数据库创建和删除表插入行数据 安装 Windows下安装,首先到下载页面,下载Windows安装软件,一般是 sqlite-dll-win32-*.zip sqlite-tools-win32-*.zip下载之后将其内容解压到同一个文件夹下,我把它们都放在了D:\…...
详解Python中的json库
目录 1. json简介2. dumps/loads3. dump/load4. jsonl格式 1. json简介 JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,用于在不同应用程序之间传递数据。它是一种文本格式,易于阅读和编写,同时也易于…...

【Spring Boot】Spring Boot源码解读与原理剖析
这里写目录标题 前言精进Spring Boot首选读物“小册”变“大书”,彻底弄懂Spring Boot全方位配套资源,学不会来找我!技术新赛道,2023领先抢跑 前言 承载着作者的厚望,掘金爆火小册同名读物《Spring Boot源码解读与原理…...
C++学习(1)
一、C概述(了解) C在C语言的基础上添加了面向对象编程和泛型编程的支持 二、helloword程序(掌握) #define _CET_SECURE_NO_WARNINGS//在开发软件visual studio编译 c文件时, visual studio认为strcpy,scanf等函数不安全的导致报…...

机器人如何有效采摘苹果?
摘要:本文利用动捕数据构建拟人运动模型,对比观察两种苹果采摘模式,并对系统性能进行全面评估,为提高机器人采摘效率提供创新方法。 近期,一项关于苹果采摘机器人的有趣研究—— "Design and evaluation of a rob…...

C# OpenCvSharp Yolov8 Detect 目标检测
效果 项目 代码 using OpenCvSharp; using OpenCvSharp.Dnn; using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms;namespace Open…...
rust数组
一、定义数组 (一)一维数组 1.指定所有元素 语法格式 let variable_name: [dataType; size] [value1,value2,value3];例如 let arr: [i32; 4] [10,20,30,40];2.指定初始值和长度 所有元素具有相同的值 语法格式 let variable_name: [dataType; siz…...

ubuntu | 安装NVIDIA套件:驱动、CUDA、cuDNN
CUDA 查看支持最高的cuda版本 nvidia-smiCUDA Version:12.2 区官网下在12.2.x最新的版本即可CUDA Toolkit Archive | NVIDIA Developer 下载安装 wget https://developer.download.nvidia.com/compute/cuda/12.2.2/local_installers/cuda_12.2.2_535.104.05_linux.run sudo…...
JAVA学习笔记
一、学习要点 java的最大优势就是跨平台; java的三个版本,javaSE标准版本,javaEE企业版本,javaME微型版本(用的比较少); JVM(Java Virtual Machine,Java虚拟机); JRE…...

车载软件架构 —— 持续集成持续交付
车载软件架构 —— 持续集成持续交付 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 对学习而言,学习之后的思考、思考之后的行动、行动之后的改变更重要,如果不盯住内层的改变量,那么在表层投…...
c++ 二元运算符重载, 以加法为例
/* * c 二元运算符重载, 以加法为例 */ #include <stdio.h> class Complex { public: int r0; // real, 实部 int v0; //virtual, 虚部 }; // 重载加法 操作符 // 可见,c2元运算符,取其左侧为第一参数,右侧为第二参数 // 返回值可以付给新的变量 C…...
KubeSphere 容器平台高可用:环境搭建与可视化操作指南
Linux_k8s篇 欢迎来到Linux的世界,看笔记好好学多敲多打,每个人都是大神! 题目:KubeSphere 容器平台高可用:环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...
React Native 导航系统实战(React Navigation)
导航系统实战(React Navigation) React Navigation 是 React Native 应用中最常用的导航库之一,它提供了多种导航模式,如堆栈导航(Stack Navigator)、标签导航(Tab Navigator)和抽屉…...

【入坑系列】TiDB 强制索引在不同库下不生效问题
文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...
linux 错误码总结
1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...

MODBUS TCP转CANopen 技术赋能高效协同作业
在现代工业自动化领域,MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步,这两种通讯协议也正在被逐步融合,形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...
Neo4j 集群管理:原理、技术与最佳实践深度解析
Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...

ElasticSearch搜索引擎之倒排索引及其底层算法
文章目录 一、搜索引擎1、什么是搜索引擎?2、搜索引擎的分类3、常用的搜索引擎4、搜索引擎的特点二、倒排索引1、简介2、为什么倒排索引不用B+树1.创建时间长,文件大。2.其次,树深,IO次数可怕。3.索引可能会失效。4.精准度差。三. 倒排索引四、算法1、Term Index的算法2、 …...
Java入门学习详细版(一)
大家好,Java 学习是一个系统学习的过程,核心原则就是“理论 实践 坚持”,并且需循序渐进,不可过于着急,本篇文章推出的这份详细入门学习资料将带大家从零基础开始,逐步掌握 Java 的核心概念和编程技能。 …...

Android15默认授权浮窗权限
我们经常有那种需求,客户需要定制的apk集成在ROM中,并且默认授予其【显示在其他应用的上层】权限,也就是我们常说的浮窗权限,那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...

ArcGIS Pro制作水平横向图例+多级标注
今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作:ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等(ArcGIS出图图例8大技巧),那这次我们看看ArcGIS Pro如何更加快捷的操作。…...