java agent开发

java agent介绍

java agent是jvm插件或者叫做代理,她是运行在main方法之前,她内定的方法名称叫premain。

java agent 概述

https://ws1.sinaimg.cn/large/afc434bdly1fplw4dc0lzj20j70ch74u.jpg

接下来我们进行开发

  1. 实现premain方法

    1
    2
    3
    4
    5
    6
    7
    package org.xxz;

    public class AgentMain {
    public static void premain(String args, Instrumentation inst) {
    System.out.println('hello java agent');
    }
    }

    上面的这段代码就完成了java agent的第一步了

  2. 打包

    这里我们使用maven的方式进行打包,请看下面的配置文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    <build>
    <finalName>java-agent</finalName>
    <plugins>
    <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
    <source>1.8</source>
    <target>1.8</target>
    <encoding>utf-8</encoding>
    </configuration>
    </plugin>
    <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-assembly-plugin</artifactId>
    <version>3.0.0</version>
    <configuration>
    <archive>
    <manifest>
    <addClasspath>true</addClasspath>
    </manifest>
    <manifestEntries>
    <Premain-Class>org.xxz.AgentMain</Premain-Class>
    </manifestEntries>
    </archive>
    <descriptorRefs>
    <descriptorRef>jar-with-dependencies</descriptorRef>
    </descriptorRefs>
    </configuration>
    <executions>
    <execution>
    <id>make-assembly</id>
    <phase>package</phase>
    <goals>
    <goal>single</goal>
    </goals>
    </execution>
    </executions>
    </plugin>
    </plugins>
    </build>
  3. 使用java agent

    新建一个maven工程,打包然后运行

    1
    java -jar demo.jar -javaagent:/apps/java-agent.jar

    执行上面的运行命令后,在我们控制台输出时就会看到hello java agent的字样哦!!!

看到这里就结束了吗?没有哦,我们来看一个小例子。。。。。。。

新建maven工程

1
2
3
4
5
6
7
8
9
10
11
java-agent
--src
--main
--java
--org.xxz
--AgentMain.java
--TimeInterceptor.java
--TraceTime.java
--resource
--test
--pom.xml

首先看看我们的pom.xml是如何配置的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
<?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.xxz</groupId>
<artifactId>java-agent</artifactId>
<version>1.0</version>

<properties>
<bytebuddy.version>1.8.0</bytebuddy.version>
<slf4j.version>1.7.25</slf4j.version>
</properties>

<dependencies>
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>${bytebuddy.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>utf-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
</manifest>
<manifestEntries>
<Premain-Class>org.xxz.AgentMain</Premain-Class>
</manifestEntries>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

这里我们使用了bytebuddy,不懂得看官可以上官方网站瞧瞧http://bytebuddy.net/

再来看看我们得AgentMain.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
package org.xxz;

import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.matcher.ElementMatchers;
import net.bytebuddy.utility.JavaModule;

import java.lang.instrument.Instrumentation;

/**
* @author tt
*/
public class AgentMain {

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

AgentBuilder.Transformer transformer = new AgentBuilder.Transformer() {
@Override
public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder,
TypeDescription typeDescription,
ClassLoader classLoader,
JavaModule javaModule) {
return builder
.method(ElementMatchers.any()) // 拦截任意方法
.intercept(MethodDelegation.to(TimeInterceptor.class)); // 委托
}
};

AgentBuilder.Listener listener = new AgentBuilder.Listener() {
@Override
public void onDiscovery(String s, ClassLoader classLoader, JavaModule javaModule, boolean b) {

}

@Override
public void onTransformation(TypeDescription typeDescription, ClassLoader classLoader, JavaModule javaModule, boolean b, DynamicType dynamicType) {

}

@Override
public void onIgnored(TypeDescription typeDescription, ClassLoader classLoader, JavaModule javaModule, boolean b) {

}

@Override
public void onError(String s, ClassLoader classLoader, JavaModule javaModule, boolean b, Throwable throwable) {

}

@Override
public void onComplete(String s, ClassLoader classLoader, JavaModule javaModule, boolean b) {

}
};

new AgentBuilder
.Default()
.type(ElementMatchers.nameStartsWith("org.xxz"))// 指定需要拦截的类
.transform(transformer)
.with(listener)
.installOn(inst);

}

}

这里我们看看我们得TimeInterceptor.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package org.xxz;

import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.implementation.bind.annotation.SuperCall;

import java.lang.reflect.Method;
import java.util.concurrent.Callable;

/**
* @author tt
*/
public class TimeInterceptor {

@RuntimeType
public static Object interceptor(@Origin Class clazz,
@Origin Method method,
@SuperCall Callable<?> callable) throws Exception {

TraceTime traceTime = method.getAnnotation(TraceTime.class);

if (traceTime == null) {
return callable.call();
}

long start = System.currentTimeMillis();
try {
// 原有函数执行
return callable.call();
} finally {
System.out.println(clazz.getSimpleName() + "#" + method.getName() + " cost " + (System.currentTimeMillis() - start) + "ms");
}
}

}

最后就是我们得注解了TraceTime.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package org.xxz;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* @author tt
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TraceTime {
}

到这里我们得java-agent就开发完成了。。。。。是不是很简单啊。。。。

接下来,我们看看使用方式咯。。。。。

1
2
3
4
5
6
7
8
9
10
11
12
java-agent-test
--src
--main
--java
--org.xxz
--test
--AgentMainTest.java
--Demo.java
--TraceTime.java
--resource
--test
--pom.xml

依旧先看我们得pom文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?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.xxz</groupId>
<artifactId>java-agent-test</artifactId>
<version>1.0</version>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>utf-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>

这里的pom文件上面的简单多了。

看看我们的测试类AgentMainTest.java

1
2
3
4
5
6
7
8
9
10
11
package org.xxz.test;
/**
* @author tt
*/
public class AgentMainTest {

public static void main(String[] args) throws Exception {
Demo demo = new Demo();
demo.print("agent");
}
}

看看我们的Demo.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package org.xxz.test;

import org.xxz.TraceTime;

/**
* @author tt
*/
public class Demo {

@TraceTime
public void print(String string) throws InterruptedException {
Thread.sleep(100L);
System.out.println("hello " + string);
}
}

这里还少了一个TraceTime.java,把上面的拷贝过来哦。。。。注意包的结构要一样哦。。。。。

上面介绍了如何命令行使用java-agent.jar,这里我们介绍如何再IDE中使用,要上图了哦。。。。。

image

好了,今天的文章到这里就结束了。。。

最后还来一张运行结果吧。。。。

image