相关文章推荐
直爽的烤红薯  ·  定了!黄浦江苏州河两岸,未来十年发展规划出炉 ...·  7 月前    · 
玩手机的鸡蛋面  ·  Openwrt - 随笔分类 - ...·  1 年前    · 
完美的油条  ·  能否击退“集成灶刺客”!?分享小米集成灶S1 ...·  1 年前    · 
没读研的奔马  ·  Linux进程调度-应用内核设置调度策略和优 ...·  2 年前    · 
独立的熊猫  ·  【童年经典】ろせいしょものがたり中文游戏视频 ...·  2 年前    · 
Code  ›  Java Agent + javassist 调试demo_Saleson的博客
https://blog.csdn.net/Mr_rain/article/details/124148243
销魂的自行车
2 年前
    • 代码准备
      • java代码如下
      • 各module的 pom.xml
    • agent 接入的两种方式
      • 1、启动时接入
      • 2、运行时接入
    • Idea调试
      • 本地module调试
      • 本地lib调试
      • 采用jdwp进行调试

      本次实验在工程中新增了3个module,4个类

      Module Class Describe
      learn-main Handle 代理实验类
      learn-main HandleMain 实验应用main class
      learn-module AgentLauncher agent Premain class
      learn-agent-main AgentmainAttachMain attach VirtualMachine main class

      java代码如下

      Handle:

      public class Handle {
          public void invoke() {
              System.out.println("Handle.invoke....");
          public void call() {
              System.out.println("Handle.call....");
          public String hello(){
              return "hello ";
      

      HandleMain:

      import java.util.Objects;
      import java.util.Scanner;
      import java.util.concurrent.Callable;
      public class HandleMain {
          public static void main(String[] args) throws Exception {
              Handle handle = new Handle();
              Scanner scanner = new Scanner(System.in);
              while (true) {
                  String txt = scanner.next();
                  if (Objects.equals("exit", txt)) {
                      break;
                  handle.invoke();
                  handle.call();
                  System.out.println("Handle.hello():" + handle.hello());
      
      import javassist.*;
      import javassist.bytecode.CodeAttribute;
      import java.io.ByteArrayInputStream;
      import java.lang.instrument.ClassFileTransformer;
      import java.lang.Exception;
      import java.lang.instrument.Instrumentation;
      import java.lang.reflect.Method;
      import java.security.ProtectionDomain;
      import java.util.Objects;
      import java.util.concurrent.Callable;
      public class AgentLauncher {
      	// 启动时接入
          public static void premain(String agentArgs, Instrumentation inst) {
              System.out.println("premain.agentArgs: " + agentArgs);
              handle(agentArgs, inst);
      	// 运行时接入(VirtualMachine attach)
          public static void agentmain(String agentArgs, Instrumentation inst) throws Exception {
              System.out.println("agentmain.agentArgs: " + agentArgs);
              handle(agentArgs, inst);
      		System.out.println("isRetransformClassesSupported:" + inst.isRetransformClassesSupported());
              System.out.println("isRedefineClassesSupported:" + inst.isRedefineClassesSupported());
      		// 重新transform已经加载的类
              inst.retransformClasses(
                      Class.forName("com.saleson.learn.java.agent.Handle")
          private static void handle(String agentArgs, Instrumentation inst) {
              System.out.println("agentArgs: " + agentArgs);
              if (StringUtils.isBlank(agentArgs)) {
                  throw new NullPointerException("agentArgs is null.");
              String[] args = agentArgs.split(",");
              if (args.length > 3) {
                  throw new IllegalArgumentException("agentArgs is illegal.");
              String mode, className = null, methodName = null;
              if (args.length == 1) {
                  mode = args[
      
      
      
      
          
      0];
              } else if (args.length == 2) {
                  mode = args[0];
                  className = args[1];
              } else {
                  mode = args[0];
                  className = args[1];
                  methodName = args[2];
      		javassistTransform(inst, className, methodName);
          private static void javassistTransform(Instrumentation instrumentation, String className, String methodName) {
              instrumentation.addTransformer(new ClassFileTransformer() {
                  @Override
                  public byte[] transform(ClassLoader loader,
                                          String cn, Class<?> classBeingRedefined,
                                          ProtectionDomain protectionDomain,
                                          byte[] classfileBuffer) throws IllegalClassFormatException {
                      if (Objects.equals(className.replaceAll("\\.", "/"), cn)) {
                          try {
                              return javassistTransform(loader, classfileBuffer, methodName);
                          } catch (Throwable t) {
                              throw new IllegalClassFormatException(t.getClass().getName() + " -> " + t.getMessage());
                      return classfileBuffer;
              }, true);
          private static byte[] javassistTransform(ClassLoader loader, byte[] classfileBuffer, String methodName) throws Exception {
              ClassPool pool = ClassPool.getDefault();
              pool.appendClassPath(new LoaderClassPath(loader));
              CtClass ctClass = pool.makeClass(new ByteArrayInputStream(classfileBuffer));
              if ("*".equals(methodName) || StringUtils.isBlank(methodName)) {
                  CtMethod[] ctmethods = ctClass.getMethods();
                  for (CtMethod ctMethod : ctmethods) {
                      CodeAttribute ca = ctMethod.getMethodInfo2().getCodeAttribute();
                      if (ca == null) {
                          continue;
                      if (!ctMethod.isEmpty()) {
                          ctMethod.insertBefore("System.out.println(\"hello Im agent : " + ctMethod.getName() + "\");");
                  return ctClass.toBytecode();
              CtMethod ctMethod = ctClass.getDeclaredMethod(methodName);
              ctMethod.insertBefore("System.out.println(\"hello Im agent : " + ctMethod.getName() + "\");");
              return ctClass.toBytecode();
      

      AgentmainAttachMain:

      import com.saleson.learn.util.Utils;
      import com.sun.tools.attach.VirtualMachine;
      import com.sun.tools.attach.VirtualMachineDescriptor;
      import java.util.List;
      public class AgentmainAttachMain {
          public static void main(String[] args) {
              // check args
              if (args.length == 2 && !Utils.isBlank(args[0]) && !Utils.isBlank(args[1])) {
                  unsafeExec(() -> attachAgent(args[0], args[1]));
                  return;
              } else if (args.length != 3 || Utils.isBlank(args[0]) || Utils.isBlank(args[1]) || Utils.isBlank
      
      
      
      
          
      (args[2])) {
                  throw new IllegalArgumentException("illegal args");
              unsafeExec(() -> attachAgent(args[0], args[1], args[2]));
          // 从表列表查找运行HandleMain的jvm pid
          private static void attachAgent(String agentJarPath, String cfg) throws Exception {
              List<VirtualMachineDescriptor> list = VirtualMachine.list();
              for (VirtualMachineDescriptor descriptor : list) {
                  if (descriptor.displayName().endsWith("HandleMain")) {
                      VirtualMachine virtualMachine = VirtualMachine.attach(descriptor.id());
                      virtualMachine.loadAgent(agentJarPath, cfg);
                      virtualMachine.detach();
          // 指定jvm pid 加载Agent
          private static void attachAgent(String targetJvmPid, String agentJarPath, String cfg) throws Exception {
              VirtualMachine vmObj = VirtualMachine.attach(targetJvmPid);
              if (vmObj != null) {
                  vmObj.loadAgent(agentJarPath, cfg);
                  vmObj.detach();
           * 获取异常的原因描述
           * @param t 异常
           * @return 异常原因
          public static String getCauseMessage(Throwable t) {
              if (null != t.getCause()) {
                  return getCauseMessage(t.getCause());
              return t.getMessage();
          interface Exec {
              void exec() throws Throwable;
          private static void unsafeExec(Exec exec) {
              try {
                  exec.exec();
              } catch (Throwable t) {
                  t.printStackTrace();
                  System.err.println("load jvm failed : " + getCauseMessage(t));
                  System.exit(-1);
      

      各module的 pom.xml

      learn-module/pom.xml

          <dependencies>
      		<dependency>
                  <groupId>org.javassist</groupId>
                  <artifactId>javassist</artifactId>
              </dependency>
          </dependencies>
          <build>
              <plugins>
                  <plugin>
                      <!-- maven 打包插件 打原始jar包 第三方依赖打入jar包中-->
                      <artifactId>maven-assembly-plugin</artifactId>
                      <configuration>
                          <archive>
                              <manifestEntries>
      <!--                            <Class-Path>.</Class-Path>-->
                                  <Premain-Class>com.saleson.learn.agent.instrument.AgentLauncher</Premain-Class>
                                  <Agent-Class>com.saleson.learn.agent.instrument.AgentLauncher</Agent-Class>
                                  <Can-Redefine-Classes>true</Can-Redefine-Classes>
                                  <Can-Retransform-Classes>true</Can-Retransform-Classes>
                              </manifestEntries>
                          </archive>
                          <descriptorRefs>
                              <descriptorRef>jar-with-dependencies</descriptorRef>
                          </descriptorRefs>
                      </configuration>
                      <executions>
                          <execution>
                              <id>make-assembly</id> <!-- this is used for inheritance merges -->
                              <phase>package</phase> <!-- 指定在打包节点执行jar包合并操作 -->
                              <goals>
                                  <goal>single</goal>
                              </goals>
                          </execution>
                      </executions>
                  </plugin>
              </plugins>
          </build>
      

      learn-agent-main/pom.xml

          <properties>
              <tools-jar>${java.home}/../lib/tools.jar</tools-jar>
          </properties>
          <dependencies>
              <dependency>
                  <groupId>com.sun</groupId>
                  <artifactId>tools</artifactId>
                  <version>1.8</version>
                  <scope>system</scope>
                  <systemPath>${tools-jar}</systemPath>
              </dependency>
          </dependencies>
          <build>
              <plugins>
                  <plugin>
                      <groupId>org.apache.maven.plugins</groupId>
                      <artifactId>maven-jar-plugin</artifactId>
                      <version>3.1.2</version>
                      <configuration>
                          <archive>
                              <manifest>
                                  <mainClass>com.saleson.learn.agent.AgentmainAttachMain</mainClass>
                              </manifest>
                              <manifestEntries>
                                  <Class-Path>. ${java.home}/../lib/tools.jar</Class-Path>
                              </manifestEntries>
                          </archive>
                      </configuration>
                  </plugin>
              </plugins>
          </build>
      

      在项目下执行mvn clean install 命令打包编译。

      agent 接入的两种方式

      1、启动时接入

      启动脚本的格式

      java -javaagent:{agent.jar 路径}[={agent 参数}] {mainClass}
      

      启动脚本:

      $ java -javaagent:../../learn-module/target/learn-module-1.0-SNAPSHOT-jar-with-dependencies.jar=com.saleson.learn.java.agent.Handle,*  -jar learn-main-1.0-SNAPSHOT.jar
      

      2、运行时接入

      先启动运行HandleMain类

      $ java -jar learn-main-1.0-SNAPSHOT.jar
      

      然后通过ps命令查看jvm 的pid

      $ ps -aux | grep learn-main
      saleson          57375   0.0  0.0  4268776    828 s004  S+    6:41下午   0:00.00 grep learn-main
      saleson          57340   0.0  0.2 10035276  30568 s003  S+    6:41下午   0:00.17 java -jar learn-main-1.0-SNAPSHOT.jar
      

      运行AgentmainAttachMain类,完成agent运行时的接入
      运行脚本格式:

      java -jar {attachMain.jar} {pid} {agent.jar绝对路径} {agent参数}
      

      运行脚本:

      $ java -jar learn-agent-main-1.0-SNAPSHOT.jar 57340 /Users/saleson/IdeaProjects/learn/learn-module/target/learn-module-1.0-SNAPSHOT-jar-with-dependencies.jar com.saleson.learn.java.agent.Handle,*
      

      Idea调试

      本地module调试

      添加Debug Configuration
      在这里插入图片描述

      FieldContent
      Main classcom.saleson.learn.java.agent.HandleMain
      VM options-javaagent:learn-module/target/learn-module-1.0-SNAPSHOT.jar=com.saleson.learn.java.agent.Handle,*

      在AgentLauncher.premain()方法中设置断点,运行Debug
      在这里插入图片描述

      本地lib调试

      在另一个项目中新增Main class和实验所用的类:

      EHandle.java
      EHandleMain.java

      将learn-module-1.0-SNAPSHOT.jar以lib的形式添加到Idea工程中,操作路径:
      File > Project Structure > Project Settings > Libraries
      在这里插入图片描述

      添加Debug Configuration
      在这里插入图片描述

      FieldContent
      Main classcom.saleson.java.agent.EHandleMain
      VM options-javaagent:/Users/saleson/IdeaProjects/learn/learn-module/target/learn-module-1.0-SNAPSHOT.jar=com.saleson.learn.java.agent.Handle,*

      在AgentLauncher.premain()方法中设置断点,运行Debug
      在这里插入图片描述

      采用jdwp进行调试

      接下来使用运行时接入的方式来实验使用jdwp进行远程调试

      添加jdwp参数运行learn-main-1.0-SNAPSHOT.jar

      $ java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5050 -jar learn-main-1.0-SNAPSHOT.jar
      

      在AgentLauncher.agentmain()方法中设置断点,再运行AgentmainAttachMain类(不要忘记pid等参数)
      在这里插入图片描述

      在learn工程(agent源码)中添加Remote Debug Configuration
      在这里插入图片描述

      运行learn-main-1.0-SNAPSHOT.jar的命令窗口将日志打印出来
      在这里插入图片描述

      Java程序员必知:深入理解Instrument
      本地调试和远程调试javaagent的premain方法
      Java Agent(二)Attach机制及运行时加载agent
      【精通 JVM 原理】浅析 JavaAgent & Instrumentation 机制
      Java Agent基本简介和使用
      javaagent使用指
      Javaagent使用指南

      java agent是基于java instrument实现,instrument的底层实现依赖于JVMTI,也就是JVM Tool Interface。文章目录代码准备java代码如下各module的 pom.xmlagent 接入的两种方式1、启动时接入2、运行时接入Idea调试本地module调试本地lib调试采用jdwp进行调试参考代码准备本次实验在工程中新增了3个module,4个类ModuleClassDescribelearn-mainHandle代理实验类
      好久没写了,伸个懒腰来~~ javaagent又称java探针,结合javassist或asm等框架对字节码文件进行操作,从而更优雅的实现“AOP”等功能,减少对原代码的侵入性等。从而我们可以借此来实现微服务等的全链路追踪以及项目环境隔离等功能。好了话不多说,直接写示例吧: 示例分为两个项目:1、agent项目:agentdemo,2、被代理项目agentclient 我们要做的是:用agent项...
      1、需求说明 需求是在程序运行期间,向某个类的某个方法前、后加入某段业务代码,或者直接替换整个方法的业务逻辑,即业务方法客制化。注意是运行期间动态更改,做到无侵入,而不是事先在代码中写死切入点或逻辑。 拿到这个需求,首先想到的是使用 spring aop 技术,但这种方式需要事先在方法上加注解进行拦截,可我们在服务启动前并不知道要拦截哪些方法。或者直接拦截所有方法,但这样或多或少都会有一些性能问题,每次方法调用时,都会进入切面,需要判断是否需要对这个方法做客制化,而判断的规则以及客制
      本文介绍一下,当下比较基础但是使用场景却很多的一种技术,稍微偏底层点,就是字节码插桩技术了...,如果之前大家熟悉了asm,cglib以及javassit等技术,那么下面说的就很简单了...,因为下面要说的功能就是基于javassit实现的,接下来先从javaagent的原理说起,最后会结合一个完整的实例演示实际中如何使用。 1、什么是javassist? Javassist是一个开源的分析、编辑和创建Java字节码的类库。其主要的特点,在于简单,而且快速。直接使用java编码的形式,而不需要了解虚拟机
      JavaAgent是一种Java技术,它可以在Java应用程序运行时动态地修改字节码。JavaAgent通常用于监视和调试Java应用程序,或者用于实现AOP(面向切面编程)等高级功能。 Javassist是一个Java字节码操作库,它可以在运行时动态地修改Java字节码。Javassist提供了一组API,可以让开发人员在不了解底层字节码的情况下,轻松地修改类的结构和行为。Javassist通常用于实现动态代理、AOP等高级功能。
      failed to launch process in the docker container on mac m2, and return message “could not launch pro mac m2 编译dubbo3.1.x版本报Missing:com.google.protobuf:protoc:exe:osx-aarch_64 Idea remote debug
 
推荐文章
直爽的烤红薯  ·  定了!黄浦江苏州河两岸,未来十年发展规划出炉_新闻动态_规划资源局
7 月前
玩手机的鸡蛋面  ·  Openwrt - 随笔分类 - K_Code - 博客园
1 年前
完美的油条  ·  能否击退“集成灶刺客”!?分享小米集成灶S1的安装与使用_集成灶_什么值得买
1 年前
没读研的奔马  ·  Linux进程调度-应用内核设置调度策略和优先级 - 知乎
2 年前
独立的熊猫  ·  【童年经典】ろせいしょものがたり中文游戏视频_哔哩哔哩_bilibili
2 年前
今天看啥   ·   Py中国   ·   codingpro   ·   小百科   ·   link之家   ·   卧龙AI搜索
删除内容请联系邮箱 2879853325@qq.com
Code - 代码工具平台
© 2024 ~ 沪ICP备11025650号