Java Agent探针技术详解示例

 更新时间:2022年06月16日 08:46:36   作者:hi wei  
这篇文章主要介绍了Java Agent 探针技术详情,Java 中的 Agent 技术可以让我们无侵入性的去进行代理,最常用于程序调试、热部署、性能诊断分析等场景,下文更多相关资料,感兴趣的小伙伴可以参考一下

什么是java agent?

在JVM中运行中,类是通过classLoader加载.class文件进行生成的。在类加载加载.class文件生成对应的类对象之前时,我们可以通过修改.class文件内容(就是字节码修改技术),达到修改类的目的。JDK提供了对字节码进行操作的一系列api,而使用这些api开发出的程序就可以称之为java agent。

java agent能做什么?

不修改目标应用达到代码增强的目的,就好像spring的aop一样,但是java agent是直接修改字节码,而不是通过创建代理类。例如skywalking就是使用java agent技术,为目标应用代码植入监控代码,监控代码进行数据统计上报的。这种方式实现了解耦,通用的功能。

文末附上了我写的一款接口mock agent。感兴趣的可以看看,学习一波。

使用示例

入门

创建maven工程

创建一个简单的maven工程:

添加agent类:

package com.hiwei;
import java.lang.instrument.Instrumentation;
public class DemoAgent {
    /**
     * 该方法在main方法之前运行
     */
    public static void premain(String arg, Instrumentation instrumentation) {
        System.out.println("执行premain方法");
    }
}

pom.xml打包配置

添加打包插件:

      <plugins>
        <plugin>
          <artifactId>maven-shade-plugin</artifactId>
          <executions>
            <execution>
              <phase>package</phase>
              <goals>
                <goal>shade</goal>
              </goals>
              <configuration>
                <shadedArtifactAttached>false</shadedArtifactAttached>
                <createDependencyReducedPom>true</createDependencyReducedPom>
                <createSourcesJar>true</createSourcesJar>
                <shadeSourcesContent>true</shadeSourcesContent>
                <transformers>
                  <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                    <manifestEntries>
                      <!-- 包含premain方法的类 -->
                      <Premain-Class>${premain.class}</Premain-Class>
                      <!-- 对目标类字节码操作权限 -->
                      <Can-Redefine-Classes>${can.redefine.classes}</Can-Redefine-Classes>
                      <!-- 对目标类字节码操作权限 -->
                      <Can-Retransform-Classes>${can.retransform.classes}</Can-Retransform-Classes>
                    </manifestEntries>
                  </transformer>
                </transformers>
              </configuration>
            </execution>
          </executions>
        </plugin>
      </plugins>

将工程打成jar包,就可以使用了。打包很简单:

在target目录下就出现了jar包:

使用agent

创建测试类:

package com.hiwe.demo.cache;
public class TestCache {
    public static void main(String[] args) {
        System.out.println("测试类");
    }
}

添加javaagent参数配置:

-javaagent:D:\person-project\demo-agent\target\demo-agent-1.0-SNAPSHOT.jar

运行测试类:

进阶(一款接口mock数据小插件)

流程

使用java agent为接口mock数据

agent类:

package com.hiwei.test.agent;
import com.hiwei.test.agent.utils.StringUtils;
import java.io.*;
import java.lang.instrument.Instrumentation;
import java.nio.charset.StandardCharsets;
public class DemoAgent {
    /**
     * 该方法在main方法之前运行
     * @param arg 文件路径
     * @param instrumentation
     */
    public static void premain(String arg, Instrumentation instrumentation) {
        //读取mock文件
        try {
            FileInputStream file = new FileInputStream(arg);
            BufferedReader rd = new BufferedReader(new InputStreamReader(file, StandardCharsets.UTF_8));
            String configStr = readToString(rd);
            if(StringUtils.isNotEmpty(configStr)){
                MockConfig.parseConfig(configStr);
                instrumentation.addTransformer(new DemoTransformer());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    private static String readToString(Reader rd) throws IOException {
        StringBuilder sb = new StringBuilder();
        int i;
        while ((i = rd.read()) != -1) {
            sb.append((char) i);
        }
        return sb.toString();
    }
}

mock数据配置类

package com.hiwei.test.agent;
import com.alibaba.fastjson.JSONObject;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class MockConfig {
    private static Map<String, Map<String, Object>> mockData = new HashMap<>();
    public static void parseConfig(String configStr) {
        JSONObject config = JSONObject.parseObject(configStr);
        Set<String> classNameSet = config.keySet();
        classNameSet.forEach(className -> {
            Map<String, Object> methodMock = new HashMap<>();
            JSONObject methods = config.getJSONObject(className);
            Set<String> mSet = methods.keySet();
            mSet.forEach(m -> {
                methodMock.put(m, methods.get(m));
            });
            mockData.put(className, methodMock);
        });
    }
    public static boolean isMock(String className) {
        return !(mockData.get(className) == null);
    }
    public static Object getMockData(String className, String methodName) {
        Map<String, Object> stringJSONObjectMap = mockData.get(className);
        if (stringJSONObjectMap != null) {
            return stringJSONObjectMap.get(methodName);
        }
        return null;
    }
}

字节码操作类

package com.hiwei.test.agent;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import java.lang.instrument.ClassFileTransformer;
import java.security.ProtectionDomain;
public class DemoTransformer implements ClassFileTransformer {
    @Override
    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) {
        ClassPool classPool = ClassPool.getDefault();
        String realClassName = className.replaceAll("/", ".");
        //获取类
        if(MockConfig.isMock(realClassName)){
            try {
                CtClass ctClass = classPool.get(realClassName);
                CtMethod[] declaredMethods = ctClass.getDeclaredMethods();
                for (CtMethod m : declaredMethods) {
                    String mName = m.getName();
                    Object mockData = MockConfig.getMockData(realClassName, mName);
                    if(mockData!=null){
                        String mock = String.format("if(true){%s}", mockData);
                        m.insertBefore(mock);
                    }
                }
                return ctClass.toBytecode();
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }
        return classfileBuffer;
    }
}

配置文件:mock.json

{
  "com.hiwei.test.DemoService":{  -- 类名
    "add":"return 123;",   -- 方法名:mock代码
    "delete":"return \"mock delete\";",
    "getUser": "com.hiwei.test.User user2 = new com.hiwei.test.User();return user2;"
  }
}

使用

配置参数:

-javaagent:D:\person-project\java-agent\demo-agent\target\demo-agent-1.0-SNAPSHOT.jar=D:\person-project\java-agent\agent-test\mock.json

源码链接:https://github.com/hiwei-zhang/java-agent

到此这篇关于Java Agent探针技术详解示例的文章就介绍到这了,更多相关Java Agent内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java打印九九乘法表代码详情

    Java打印九九乘法表代码详情

    这篇文章主要介绍了Java打印九九乘法表,使用了双重for循环,使用do{}while()实现打印九九乘法表这些好玩的语法实现,感兴趣的小伙伴可参考下面文章内容
    2021-09-09
  • 使用logback实现日志打印过滤

    使用logback实现日志打印过滤

    这篇文章主要介绍了使用logback实现日志打印过滤的操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-07-07
  • SprintBoot深入浅出讲解场景启动器Starter

    SprintBoot深入浅出讲解场景启动器Starter

    本篇文章将和大家分享一下 Spring Boot 框架中的 Starters 场景启动器的内容,关于 Starters 具体是用来做什么的,以及在开发 Spring Boot项目前,要如何自定义一个 Starters 场景启动器
    2022-06-06
  • IDEA中安装和使用Lombok插件的方法

    IDEA中安装和使用Lombok插件的方法

    Lombok是一个可以通过简单的注解形式来帮助我们简化消除一些必须有但显得很臃肿的Java代码的工具,通过使用对应的注解,可以在编译源码的时候生成对应的方法,本文重点给大家介绍IDEA中安装和使用Lombok插件的方法,感兴趣的朋友一起看看吧
    2021-06-06
  • Java中的ConcurrentLinkedQueue使用解析

    Java中的ConcurrentLinkedQueue使用解析

    这篇文章主要介绍了Java中的ConcurrentLinkedQueue使用解析,一个基于链接节点的无界线程安全队列,此队列按照 FIFO(先进先出)原则对元素进行排序,队列的头部是队列中时间最长的元素,需要的朋友可以参考下
    2023-12-12
  • JUnit测试控制@Test执行顺序的三种方式小结

    JUnit测试控制@Test执行顺序的三种方式小结

    这篇文章主要介绍了JUnit测试控制@Test执行顺序的三种方式小结,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09
  • springBoot 插件工具热部署 Devtools的步骤详解

    springBoot 插件工具热部署 Devtools的步骤详解

    这篇文章主要介绍了springBoot 插件工具 热部署 Devtools,本文分步骤给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-10-10
  • 浅谈hashmap为什么查询时间复杂度为O(1)

    浅谈hashmap为什么查询时间复杂度为O(1)

    这篇文章主要介绍了hashmap为什么查询时间复杂度为O(1),具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-08-08
  • Java使用HttpUtils实现发送HTTP请求

    Java使用HttpUtils实现发送HTTP请求

    这篇文章主要介绍了Java使用HttpUtils实现发送HTTP请求,HTTP请求,在日常开发中,还是比较常见的,今天给大家分享HttpUtils如何使用,需要的朋友可以参考下
    2023-05-05
  • java迭代器和for循环优劣详解

    java迭代器和for循环优劣详解

    在本篇文章里小编给大家整理的是一篇关于java迭代器和for循环优劣详解内容,对此有兴趣的朋友们可以学习参考下。
    2021-01-01

最新评论