插桩java_字节码插桩技术

本文介绍了Java字节码插桩技术,包括javaagent的使用和javassist库的应用。通过javaagent,我们可以在类加载前进行拦截并插入自定义的字节码。javassist则简化了操作字节码的过程,允许我们在不理解虚拟机指令的情况下动态改变类结构。文中通过实例展示了如何使用javassist插入监控代码,以计算方法调用时间。

字节码插桩

我们知道JVM是不能直接执行.java 代码,也不能直接执行.class文件,它只能执行.class 文件中存储的指令码。这就是为什么class需要通过classLoader 装载以后才能运行。基于此机制可否在ClassLoader装载之前拦截修改class当中的内容(jvm 指令码)从而让程序中包含我们的埋点逻辑呢?答案是肯定的,但需要用到两个技术 javaagent与javassist 。前者用于拦截ClassLoad装载,后者用于操作修改class文件。

javaagent

javaagent介绍

javaagent 是java1.5之后引入的特性,其主要作用是在class 被加载之前对其拦截,以插入我们的监听字节码

a7c9d7c5db11c3b7d55684fec805b88b.png

javaagent jar包

javaagent 最后展现形式是一个Jar包,有以下特性:

1.必须 META-INF/MANIFEST.MF中指定Premain-Class 设定启agent启动类。

2.在启类需写明启动方法 public static void main(String arg,)

3.不可直接运行,只能通过 jvm 参数-javaagent:xxx.jar 附着于其它jvm 进程运行。

javaagent使用

1、编写agent方法

public classMyAgent {public static void premain(String args, Instrumentation instrumentation) throwsException {

System.out.println("Hello javaagent permain:"+args);

}

}

2、添加premain-class参数

org.apache.maven.plugins

maven-jar-plugin

2.2

${project.name}

${project.version}

com.javaagent.MyAgent

true

true

true

3、构建打包

2e7facad05a6b0eb47afc9452b3f1865.png

4、在任一JAVA应用中 添加jvm 参数并启动 -javaagent:xxx.jarjavaagent META-INF/MANIFEST.MF

参数说明:

Premain-Class:必填,agent启动

classCan-Redefine-Classes:默认为false ,是否允许重新定义

classCan-Retransform-Classes:默认为false,是否允许重置Class,重置后相当于class 从classLoade中清除,下次有需要的时候会重新装载,也会重新走Transformer 流程。

Boot-Class-Path:agent 所依赖的jar 路径,多个用空格分割

创建一个测试类MyAgentTest并运行查看结果

public classMyAgentTest {public static voidmain(String[] args) {

System.out.println("main");

}

}//运行结果:main

添加jvm参数

参数内容:-javaagent:/Users/jinyunlong/IdeaProjects/test-agent/target/test-agent-1.0-SNAPSHOT.jar=123

4763303a3b8232ae593ad7431f7cc6c9.png

再次运行测试类MyAgentTest并查看结果

2f82db3dd46aac483c145a28f452528f.png

javassist

javassist介绍

javassist是一个开源的分析、编辑和创建Java字节码的类库。其主要的优点,在于简单,而且快速。直接使用java编码的形式,而不需要了解虚拟机指令,就能动态改变类的结构,或者动态生成(注:也可以使用ASM实现,但需要会操作字节码指令,学习使用成本高)

javassist使用

使用javassist需要引入javasssist的jar包,添加内容如下:

org.javassist

javassist

3.18.1-GA

org.apache.maven.plugins

maven-jar-plugin

2.2

${project.name}

${project.version}

com.javaagent.MyAgent

true

true

javassist-3.18.1-GA.jar

true

演示插入打印当前时间

创建类MyServer

public classMyServer {publicInteger sayHello(String name,String message){

System.out.println("hello");return 0;

}

}

myAgent类

5c90421fe751925e393f88219799c770.png

创建测试类并调用MyServer中的sayHello方法

4022a20e072e0e63a9d7cc5b5e717617.png

演示计算方法调用时间

类MyAgent

public classMyAgent {public static void premain(String args, Instrumentation instrumentation) throwsException {

instrumentation.addTransformer(newClassFileTransformer() {public byte[] transform(ClassLoader loader, String className, Class>classBeingRedefined, ProtectionDomain protectionDomain,byte[] classfileBuffer) throwsIllegalClassFormatException {if(!"com/javaagent/MyServer".equals(className)){return null;

}try{returnbuildMonitorClass();

}catch(Exception e) {

e.printStackTrace();

}return null;

}

},true);

}private static byte[] buildMonitorClass() throwsException{/*** 1、拷贝一个新的方法

* 2、修改原方法名

* 3、加入监听代码*/ClassPool pool= newClassPool();

pool.appendSystemPath();

CtClass ctClass= pool.get("com.javaagent.MyServer");

CtMethod ctMethod= ctClass.getDeclaredMethod("sayHello");

CtMethod copyMethod= CtNewMethod.copy(ctMethod,ctClass,newClassMap());

ctMethod.setName("sayHello$agent");

copyMethod.setBody("{\n" +

" long begin = System.nanoTime();\n" +

" try {\n" +

" return sayHello$agent($1,$2);\n" +

" } finally {\n" +

" System.out.println(System.nanoTime() - begin);}\n" +

" }");

ctClass.addMethod(copyMethod);returnctClass.toBytecode();

}

}

修改类MyServer

public classMyServer {publicInteger sayHello(String name,String message){

System.out.println("hello name:"+name+",message:"+message);return 0;

}

}

修改测试类并运行

public classMyAgentTest {public static voidmain(String[] args) {

MyServer myServer= newMyServer();

myServer.sayHello("paul","1234");

}

}//运行结果:

hello name:paul,message:1234

186537

javassist特殊语法

7473a9032186ca65f28d7dcd93c66a93.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值