JAVA和Nginx 教程大全

网站首页 > 精选教程 正文

java通过插桩的概念来完成监控系统的设计

wys521 2024-11-07 13:44:11 精选教程 27 ℃ 0 评论

上节说了javaagent和javassist,其实javassist也是基于ASM实现的。一般人不懂得JVM指令的话,根本ASM搞不起来,也用到了访问者的设计模式,看起来跟咱们写代码不是一个套路,学习成本比较高,所以有了javassist。

源码:https://github.com/limingios/netFuture/tree/master/源码/『互联网架构』插桩处理埋点(113)/



(一)插桩

  • 修改上节源码,进行插桩

IdigAgentTest

package com.idig8;
import com.idig8.agent.test.UserServiceImpl;
import java.lang.instrument.Instrumentation;
public class IdigAgentTest {
 public static void main(String[] args) {
 System.out.println("hello world idig8!");
 UserServiceImpl userService = new UserServiceImpl();
 userService.hello();
 }
}



UserServiceImpl 增加hello方法,打印方法里面的内容

package com.idig8.agent.test;
public class UserServiceImpl {
 public UserServiceImpl(){
 System.out.println("hello world!");
 }
 public void hello(){
 String p1 ="100";
 System.out.print("p1 = "+p1);
 }
}



IdigAgent 增加插桩的方法

package com.idig8.agent.test;
import javassist.*;
import java.io.IOException;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
public class IdigAgent {
 public static void premain(String args, Instrumentation instrumentation) {
 System.out.println("premain:" + args);
 instrumentation.addTransformer(new ClassFileTransformer() {
 @Override
 public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
 ProtectionDomain protectionDomain,
 byte[] classfileBuffer) throws IllegalClassFormatException {
 if (!"com/idig8/agent/test/UserServiceImpl".equals(className)) {
 return null;
 }
 // javassist 工具 改造
 try {
 ClassPool pool = new ClassPool();
 pool.insertClassPath(new LoaderClassPath(loader));
 CtClass ctclass = pool.get("com.idig8.agent.test.UserServiceImpl");
 CtMethod method = ctclass.getDeclaredMethod("hello");
 method.insertBefore(" System.out.println(System.currentTimeMillis());");
// method.insertBefore("long begin = System.currentTimeMillis();"
// +" System.out.println(begin);");
//
// method.insertAfter(" long end = System.currentTimeMillis();\n" +
// " System.out.println(end - begin);");
 return ctclass.toBytecode();
 } catch (NotFoundException e) {
 e.printStackTrace();
 } catch (CannotCompileException e) {
 e.printStackTrace();
 } catch (IOException e) {
 e.printStackTrace();
 }
 return null;
 }
 });
 }
}



pom中添加javassist的jar包

 <dependency>
 <groupId>org.javassist</groupId>
 <artifactId>javassist</artifactId>
 <version>3.18.1-GA</version>
 </dependency>



  • 代码的执行流程

IdigAgentTest执行这个方法

IdigAgent ->IdigAgentTest->UserServiceImpl的类->UserServiceImpl被插桩启动开始的hello-> 打印hello方法体

premain:abc //IdigAgent 
hello world idig8! //IdigAgentTest
hello world! //UserServiceImpl的类
1562479947074 //UserServiceImpl被插桩启动开始的hello
p1 = 100 //打印hello方法体



  • 注意代码

加入insertBefore中的bgin 和 insertAfter 的 end 通过end-begin 但是后台报错了



javassist.CannotCompileException: [source error] no such field: begin

为什么呢,因为插桩的时候都是以代码快的形式,局部变量。



在实际开发中不用修改原有的方法,而是会新写一个方法,在新方法进行出来,调用要插桩的方法。新方法的参数和要插桩的保持一致,包括注解,参数。其实有点类似动态代理。

 public void hello$agent()
 {
 {
 long begin = System.currentTimeMillis();
 try{
 hello();
 }finally {
 long end = System.currentTimeMillis();
 System.out.println(end - begin);
 }
 }
 }



看到这里,点了关注吧!

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表