Java Instrument动态修改字节码入门-添加方法耗时监控

2024-04-25 03:58

本文主要是介绍Java Instrument动态修改字节码入门-添加方法耗时监控,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

平常在统计方法执行的耗时时长时,一般都是在方法的开头和结尾通过System.currentTimeMillis()拿到时间,然后做差值,计算耗时,这样不得不在每个方法中都重复这样的操作,现在使用Instrument,可以优雅的实现该功能。

一、编写Agent类

package com.jdktest.instrument;import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;public class AopAgentTest {static private Instrumentation _inst = null;  /** * The agent class must implement a public static premain method similar in principle to the main application entry point. * After the Java Virtual Machine (JVM) has initialized, * each premain method will be called in the order the agents were specified, * then the real application main method will be called.**/  public static void premain(String agentArgs, Instrumentation inst) {  System.out.println("AopAgentTest.premain() was called.");  /* Provides services that allow Java programming language agents to instrument programs running on the JVM.*/  _inst = inst; /* ClassFileTransformer : An agent provides an implementation of this interface in order to transform class files.*/  ClassFileTransformer trans = new AopAgentTransformer(); System.out.println("Adding a AopAgentTest instance to the JVM.");  /*Registers the supplied transformer.*/_inst.addTransformer(trans);  }  
}

代码功能详见注释。

二、编写ClassFileTransformer

ClassFileTransformer需要实现transform方法,根据自己的功能需要修改class字节码,在修改字节码过程中需要借助javassist进行字节码编辑。

javassist是一个开源的分析、编辑和创建java字节码的类库。通过使用javassist对字节码操作可以实现动态”AOP”框架。

关于java字节码的处理,目前有很多工具,如bcel,asm(cglib只是对asm又封装了一层)。不过这些都需要直接跟虚拟机指令打交道。javassist的主要的优点,在于简单,而且快速,直接使用java编码的形式,而不需要了解虚拟机指令,就能动态改变类的结构,或者动态生成类。

package com.jdktest.instrument;import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CodeConverter;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.NotFoundException;
import javassist.expr.ExprEditor;
import javassist.expr.MethodCall;public class AopAgentTransformer implements ClassFileTransformer{public byte[] transform(ClassLoader loader, String className,Class<?> classBeingRedefined, ProtectionDomain protectionDomain,byte[] classfileBuffer) throws IllegalClassFormatException {byte[] transformed = null;  System.out.println("Transforming " + className);  ClassPool pool = null;  CtClass cl = null;  try {  pool = ClassPool.getDefault();cl = pool.makeClass(new java.io.ByteArrayInputStream(  classfileBuffer));  //            CtMethod aop_method = pool.get("com.jdktest.instrument.AopMethods").
//                    getDeclaredMethod("aopMethod");
//            System.out.println(aop_method.getLongName());CodeConverter convert = new CodeConverter();if (cl.isInterface() == false) {  CtMethod[] methods = cl.getDeclaredMethods();  for (int i = 0; i < methods.length; i++) {  if (methods[i].isEmpty() == false) {  AOPInsertMethod(methods[i]);  }  }  transformed = cl.toBytecode();  }  } catch (Exception e) {  System.err.println("Could not instrument  " + className  + ",  exception : " + e.getMessage());  } finally {  if (cl != null) {  cl.detach();  }  }  return transformed;  }private void AOPInsertMethod(CtMethod method) throws NotFoundException,CannotCompileException {//situation 1:添加监控时间method.instrument(new ExprEditor() {  public void edit(MethodCall m) throws CannotCompileException {  m.replace("{ long stime = System.currentTimeMillis(); $_ = $proceed($$);System.out.println(\""+ m.getClassName() + "." + m.getMethodName()+ " cost:\" + (System.currentTimeMillis() - stime) + \" ms\");}");}}); //situation 2:在方法体前后语句
//      method.insertBefore("System.out.println(\"enter method\");");
//      method.insertAfter("System.out.println(\"leave method\");");}}

三、打包Agent jar包

因为agent依赖javassist,在build时需要加入<Boot-Class-Path>,pom文件如下:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.jdktest</groupId><artifactId>MyInstrument</artifactId><version>0.0.1-SNAPSHOT</version><packaging>jar</packaging><name>instrument</name><url>http://maven.apache.org</url><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies><dependency><groupId>javassist</groupId><artifactId>javassist</artifactId><version>3.8.0.GA</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>3.8.1</version><scope>test</scope></dependency></dependencies><build>  <plugins>  <plugin>  <groupId>org.apache.maven.plugins</groupId>  <artifactId>maven-jar-plugin</artifactId>  <version>2.2</version>  <configuration>  <archive>  <manifestEntries>  <Premain-Class>com.jdktest.instrument.AopAgentTest</Premain-Class>  <Boot-Class-Path>E:/maven-lib/javassist/javassist/3.8.0.GA/javassist-3.8.0.GA.jar</Boot-Class-Path>  </manifestEntries>  </archive>  </configuration>  </plugin>  <plugin>  <artifactId>maven-compiler-plugin </artifactId >  <configuration>  <source> 1.6 </source >  <target> 1.6 </target>  </configuration>  </plugin>  </plugins>   </build> 
</project>

四、需要添加耗时监控的client

随意编写需要添加耗时监控的代码并打jar包。

package com.jdktest.SayHello;public class Target {public static void main(String[] args) {new Target().sayHello();}public void sayHello(){System.out.println("Hello, guys!");}
}

如果sayHello方法中又调用了同类中的方法或者别的类中的方法,这些被调用的方法的耗时仍可被监控到。

五、执行

在cmd下执行如下命令:

java -javaagent:E:/workspace/MyInstrument/target/MyInstrument-0.0.1-SNAPSHOT.jar -cp E:/workspace/SayHello/target/SayHello-0.0.1-SNAPSHOT.jar com.jdktest.SayHello.Target

六、执行结果

这里写图片描述

这篇关于Java Instrument动态修改字节码入门-添加方法耗时监控的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/933638

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

流媒体平台/视频监控/安防视频汇聚EasyCVR播放暂停后视频画面黑屏是什么原因?

视频智能分析/视频监控/安防监控综合管理系统EasyCVR视频汇聚融合平台,是TSINGSEE青犀视频垂直深耕音视频流媒体技术、AI智能技术领域的杰出成果。该平台以其强大的视频处理、汇聚与融合能力,在构建全栈视频监控系统中展现出了独特的优势。视频监控管理系统EasyCVR平台内置了强大的视频解码、转码、压缩等技术,能够处理多种视频流格式,并以多种格式(RTMP、RTSP、HTTP-FLV、WebS

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

字节面试 | 如何测试RocketMQ、RocketMQ?

字节面试:RocketMQ是怎么测试的呢? 答: 首先保证消息的消费正确、设计逆向用例,在验证消息内容为空等情况时的消费正确性; 推送大批量MQ,通过Admin控制台查看MQ消费的情况,是否出现消费假死、TPS是否正常等等问题。(上述都是临场发挥,但是RocketMQ真正的测试点,还真的需要探讨) 01 先了解RocketMQ 作为测试也是要简单了解RocketMQ。简单来说,就是一个分