javaagent实战

简介: javaagent实战

前言

  • javaagent介绍:

javaagent是依赖java底层提供的一个叫instrument的JVMTI Agent。简单来说,javaagent是一个JVM的“插件”。 在java运行命令中 javaagent是一个参数,用来指定agent。

启动时加载的 JavaAgent 是 JDK1.5 之后引入的新特性,此特性为用户提供了在 JVM 将字节码文件读入内存之后,JVM 使用对应的字节流在 Java 堆中生成一个 Class 对象之前,用户可以对其字节码进行修改的能力,从而 JVM 也将会使用用户修改过之后的字节码进行 Class 对象的创建。

  • javassit介绍:

Javaassist 就是一个用来 处理 Java 字节码的类库。它可以在一个已经编译好的类中添加新的方法,或者是修改已有的方法,并且不需要对字节码方面有深入的了解。同时也可以去生成一个新的类对象,通过完全手动的方式。

javaassist 就是一个用来处理 Java 字节码的类库。
javassist 也称为动态编译,动态编译技术通过操作 class 文件,动态添加元素或者修改代码。


代码实战

1.创建maven工程、并添加相关依赖

pom.xml文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<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>org.example</groupId>
    <artifactId>jdk-agent</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.javassist</groupId>
            <artifactId>javassist</artifactId>
            <version>3.25.0-GA</version>
        </dependency>
    </dependencies>
</project>

2.编码拦截代码

  • 新建入口类,添加premain方法;
    ```java
    public class AgentDemo {

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

      System.out.println("coming in");
      instrumentation.addTransformer(new ClassConstructorTransform());
    

    }

}

premain会优先于main方法启动;

- 编码class转换代码
```java

public class ClassConstructorTransform implements ClassFileTransformer {

    @Override
    public byte[] transform(ClassLoader loader,
                            String className,
                            Class<?> classBeingRedefined,
                            ProtectionDomain protectionDomain,
                            byte[] classfileBuffer) {
        byte[] bytes = null;
        if(className.equals("com/client/Test")){
            System.out.println("...");
            ClassPool classPool = ClassPool.getDefault();
            try {
                System.out.println(classPool);
                System.out.println("......");
                String name = className.replaceAll("/","\\.");
                System.out.println(name);
                ClassClassPath classPath = new ClassClassPath(this.getClass());
                System.out.println(classPath);
                String path = loader.getResource("").getPath();
                System.out.println(path);
                //classPool.insertClassPath(classPath);
                classPool.appendClassPath(new LoaderClassPath(Thread.currentThread().getContextClassLoader()));
                CtClass ctClass = classPool.get(name);

               System.out.println(ctClass.getDeclaredConstructors().length);
                CtConstructor constructor = ctClass.getDeclaredConstructors()[0];
                System.out.println(constructor);
                constructor.setBody("System.out.println(\"实例化\");");

                CtMethod ctMethod = ctClass.getDeclaredMethod("getValue",
                        new CtClass[]{classPool.get("java.lang.String"),
                                classPool.get("java.lang.String"),
                                classPool.get("java.lang.String")});

                ctMethod.insertBefore("{   " +
                        "        System.out.println($2); "+
                        "       java.util.Map/*<String, String>*/ map = new java.util.HashMap/*<>*/(); " +
                        "        map.put(\"user_count\",\"50000\");" +
                        "        return (String)map.get($2);" +
                        "}");


                CtMethod ctMethod2 = ctClass.getDeclaredMethod("getLic",
                        new CtClass[]{classPool.get("java.lang.String")});
                ctMethod2.setBody("{   " +
                        "       java.util.Map/*<String, String>*/ map = new java.util.HashMap/*<>*/(); " +
                        "        map.put(\"begin\",\"2000-01-01\");" +
                        "        map.put(\"end\",\"2000-01-01\");" +
                        "        return map;" +
                        "}");
                CtMethod ctMethod3 = ctClass.getDeclaredMethod("getJson");
                StringBuffer stringBuffer = new StringBuffer("{");
                stringBuffer.append("String body_json = null;" +
                        "        return body_json;");
                stringBuffer.append("}");

                ctMethod3.setBody(stringBuffer.toString());
                bytes = ctClass.toBytecode();
                System.out.println("over..");
            }catch (Throwable e){
                System.err.println(e.getMessage());
                e.printStackTrace();
            }
        }
        return bytes;
    }
}

主要概念:

在 Javassist 中,类 Javaassit.CtClass 表示 class 文件。一个 CtClass (编译时类)对象可以处理一个 class 文件;。ClassPool 是 CtClass 对象的容器。它按需读取类文件来构造 CtClass 对象,并且保存 CtClass 对象以便以后使用。

CtClass 提供了 getName(),getSuperclass(),getMethods() 等方法来获取类的信息,也提供了修改类定义的方法(添加字段,添加构造函数、添加方法),同时也可以对方法体的语句进行检测。

方法由 CtMethod 对象表示。提供了 insertBefore(),insertAfter() 和 addCatch() 方法。 它们可以将用 Java 编写的代码片段插入到现有方法中。Javassist 包括一个用于处理源代码的简单编译器,它接收用 Java 编写的源代码,并将其编译成 Java 字节码,并内联方法体中。
传递给目标方法的参数使用 $1,$2,... 访问,而不是原始的参数名称。 1 表示第一个参数,2 表示第二个参数,以此类推。 这些变量的类型与参数类型相同。 0 等价于 this 指针。 如果方法是静态的,则0 不可用。

3. 打包

在pom.xml中添加打包插件配置;

  <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <version>3.3.0</version>
                <configuration>
                    <archive>
                        <manifestEntries>
                            <Premain-Class>com.jdk.agent.AgentDemo</Premain-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>one</id>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

在idea中执行package命令;

4. 使用

  • 适用idea时,在vmoptions参数中添加

    -javaagent:**\jdk-agent-1.0-SNAPSHOT.jar

  • 启动jar包时添加javaagent参数:

    java -jar -javaagent:**\jdk-agent-1.0-SNAPSHOT.jar your.jar

相关文章
|
7月前
|
监控 Java
Zabbix【部署 02】Zabbix-Java-Gateway安装配置使用(使用Zabbix-Java-Gateway通过JMX监控Java应用程序实例分享)
Zabbix【部署 02】Zabbix-Java-Gateway安装配置使用(使用Zabbix-Java-Gateway通过JMX监控Java应用程序实例分享)
358 0
|
Java
Jpom配置自动构建java
Jpom配置自动构建java
77 0
|
监控 Java 应用服务中间件
APM - Hello Javaagent
APM - Hello Javaagent
121 0
|
Java
APM - 使用JavaAgent+Javassit 插桩C3P0
APM - 使用JavaAgent+Javassit 插桩C3P0
131 0
|
消息中间件 JavaScript 小程序
聊聊大厂都在用的 JavaAgent 上
聊聊大厂都在用的 JavaAgent 上
|
监控 Java 程序员
聊聊大厂都在用的 JavaAgent 下
聊聊大厂都在用的 JavaAgent 下
|
Java 应用服务中间件 Linux
Linux环境下在Tomcat上部署JavaWeb工程
Linux环境下在Tomcat上部署JavaWeb工程
170 0
Linux环境下在Tomcat上部署JavaWeb工程
|
Java 应用服务中间件 Linux
tomcat catalina.sh JAVA_OPTS参数说明与配置
tomcat catalina.sh JAVA_OPTS参数说明与配置
458 0
|
Java Linux 程序员
JVM源码分析之javaagent原理完全解读
JVM源码分析之javaagent原理完全解读
JVM源码分析之javaagent原理完全解读