├── app
├── pom.xml
└── src
│ └── main
│ └── java
│ └── com
│ └── git
│ └── hui
│ └── java
│ ├── AttachMain.java
│ ├── BaseMain.java
│ └── DemoClz.java
├── cost-agent
├── build.sh
├── pom.xml
└── src
│ └── main
│ └── java
│ └── com
│ └── git
│ └── hui
│ └── agent
│ ├── CostAgent.java
│ └── CostTransformer.java
├── pom.xml
├── readme.md
└── simple-agent
├── build.sh
├── pom.xml
└── src
└── main
├── java
└── com
│ └── git
│ └── hui
│ └── agent
│ └── SimpleAgent.java
└── resources
└── META-INF
└── MANIFEST.MF
/app/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | java-agent
7 | com.git.hui.demo
8 | 1.0-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | app
13 |
14 |
15 |
--------------------------------------------------------------------------------
/app/src/main/java/com/git/hui/java/AttachMain.java:
--------------------------------------------------------------------------------
1 | package com.git.hui.java;
2 |
3 | import com.sun.tools.attach.AgentInitializationException;
4 | import com.sun.tools.attach.AgentLoadException;
5 | import com.sun.tools.attach.AttachNotSupportedException;
6 | import com.sun.tools.attach.VirtualMachine;
7 |
8 | import java.io.IOException;
9 |
10 | /**
11 | * Created by @author yihui in 19:48 20/3/15.
12 | */
13 | public class AttachMain {
14 |
15 |
16 | public static void main(String[] args)
17 | throws IOException, AgentLoadException, AgentInitializationException, AttachNotSupportedException {
18 | // 用jps -l 查出目标应用的进程号,替换下面的参数
19 | VirtualMachine vm = VirtualMachine.attach("36633");
20 | // 用你自己的agent绝对地址替换
21 | vm.loadAgent(
22 | "/Users/user/Project/GitHub/study-demo/java-agent/target/java-agent-1.0-SNAPSHOT-jar-with-dependencies.jar");
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/app/src/main/java/com/git/hui/java/BaseMain.java:
--------------------------------------------------------------------------------
1 | package com.git.hui.java;
2 |
3 | /**
4 | * Created by @author yihui in 19:41 20/3/15.
5 | */
6 | public class BaseMain {
7 |
8 | public int print(int i) {
9 | System.out.println("i: " + i);
10 | try {
11 | Thread.sleep(1000);
12 | } catch (InterruptedException e) {
13 | e.printStackTrace();
14 | }
15 |
16 | if (i > 5) {
17 | // 主要是为了演示抛异常,也会执行方法耗时统计的case
18 | throw new RuntimeException();
19 | }
20 |
21 | return i + 2;
22 | }
23 |
24 | public void run() {
25 | int i = 1;
26 | while (true) {
27 | try {
28 | i = print(i);
29 | } catch (Exception e) {
30 | e.printStackTrace();
31 | }
32 | }
33 | }
34 |
35 | public static void main(String[] args) {
36 | BaseMain main = new BaseMain();
37 | main.run();
38 | //
39 | // DemoClz demoClz = new DemoClz();
40 | // int cnt = 0;
41 | // for (int i = 0; i < 20; i++) {
42 | // if (++cnt % 2 == 0) {
43 | // i = demoClz.print(i);
44 | // } else {
45 | // i = demoClz.count(i);
46 | // }
47 | // }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/app/src/main/java/com/git/hui/java/DemoClz.java:
--------------------------------------------------------------------------------
1 | package com.git.hui.java;
2 |
3 | /**
4 | * Created by @author yihui in 22:07 20/3/15.
5 | */
6 | public class DemoClz {
7 |
8 | public int print(int i) {
9 | System.out.println("print: " + i);
10 | try {
11 | Thread.sleep(1000);
12 | } catch (InterruptedException e) {
13 | e.printStackTrace();
14 | }
15 | return i + 2;
16 | }
17 |
18 | public int count(int i) {
19 | System.out.println("count: " + i);
20 | try {
21 | Thread.sleep(50);
22 | } catch (InterruptedException e) {
23 | e.printStackTrace();
24 | }
25 | return i + 1;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/cost-agent/build.sh:
--------------------------------------------------------------------------------
1 | mvn assembly:assembly
2 |
--------------------------------------------------------------------------------
/cost-agent/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | java-agent
7 | com.git.hui.demo
8 | 1.0-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | cost-agent
13 |
14 |
15 |
16 | javassist
17 | javassist
18 | 3.12.1.GA
19 |
20 |
21 |
22 |
23 |
24 |
25 | org.apache.maven.plugins
26 | maven-assembly-plugin
27 |
28 |
29 | jar-with-dependencies
30 |
31 |
32 |
33 | com.git.hui.agent.CostAgent
34 | com.git.hui.agent.CostAgent
35 | true
36 | true
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | attached
45 |
46 | package
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/cost-agent/src/main/java/com/git/hui/agent/CostAgent.java:
--------------------------------------------------------------------------------
1 | package com.git.hui.agent;
2 |
3 | import java.lang.instrument.Instrumentation;
4 |
5 | /**
6 | * Created by @author yihui in 10:46 20/3/16.
7 | */
8 | public class CostAgent {
9 |
10 | /**
11 | * jvm 参数形式启动,运行此方法
12 | *
13 | * manifest需要配置属性Premain-Class
14 | *
15 | * @param agentArgs
16 | * @param inst
17 | */
18 | public static void premain(String agentArgs, Instrumentation inst) {
19 | System.out.println("premain");
20 | customLogic(inst);
21 | }
22 |
23 | /**
24 | * 动态 attach 方式启动,运行此方法
25 | *
26 | * manifest需要配置属性Agent-Class
27 | *
28 | * @param agentArgs
29 | * @param inst
30 | */
31 | public static void agentmain(String agentArgs, Instrumentation inst) {
32 | System.out.println("agentmain");
33 | customLogic(inst);
34 | }
35 |
36 | /**
37 | * 统计方法耗时
38 | *
39 | * @param inst
40 | */
41 | private static void customLogic(Instrumentation inst) {
42 | inst.addTransformer(new CostTransformer(), true);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/cost-agent/src/main/java/com/git/hui/agent/CostTransformer.java:
--------------------------------------------------------------------------------
1 | package com.git.hui.agent;
2 |
3 | import javassist.ClassPool;
4 | import javassist.CtClass;
5 | import javassist.CtMethod;
6 |
7 | import java.io.ByteArrayInputStream;
8 | import java.lang.instrument.ClassFileTransformer;
9 | import java.security.ProtectionDomain;
10 |
11 | /**
12 | * Created by @author yihui in 19:58 20/3/15.
13 | */
14 | public class CostTransformer implements ClassFileTransformer {
15 | @Override
16 | public byte[] transform(ClassLoader loader, String className, Class> classBeingRedefined,
17 | ProtectionDomain protectionDomain, byte[] classfileBuffer) {
18 | if (!className.startsWith("com/git/hui/java/")) {
19 | return classfileBuffer;
20 | }
21 |
22 | CtClass cl = null;
23 | try {
24 | ClassPool classPool = ClassPool.getDefault();
25 | cl = classPool.makeClass(new ByteArrayInputStream(classfileBuffer));
26 |
27 | for (CtMethod method : cl.getDeclaredMethods()) {
28 | // 我们统计一下所有的方法调用耗时
29 | method.addLocalVariable("start", CtClass.longType);
30 | method.insertBefore("start = System.currentTimeMillis();");
31 | String methodName = method.getLongName();
32 | // 请注意第二个参数,设置为true,则表示即便抛出异常了,下面的代码也会执行;相当于将它封装在finally里面了
33 | method.insertAfter("System.out.println(\"" + methodName + " cost: \" + (System" +
34 | ".currentTimeMillis() - start));", true);
35 | }
36 |
37 | byte[] transformed = cl.toBytecode();
38 | return transformed;
39 | } catch (Exception e) {
40 | e.printStackTrace();
41 | }
42 | return classfileBuffer;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | com.git.hui.demo
8 | java-agent
9 | pom
10 | 1.0-SNAPSHOT
11 |
12 | simple-agent
13 | app
14 | cost-agent
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | ## java-agent
2 |
3 | 提供了一个基于idea + maven构建java agent开发测试环境的项目,其中simple-agent子工程演示了一个hello world版本的java agent项目,并提供了两种打包方式,直接执行`build.sh`即可在target下获取到可用的agent包
4 |
5 | app项目作为测试目标程序,同样提供了两种使用姿势,通过-jvm参数指定agent,以及通过attach方式挂载目标进程
6 |
7 | 更多详细内容,请参考博文:
8 |
9 | - [200303-如何优雅的在java中统计代码块耗时](https://mp.weixin.qq.com/s/3cw3WsTUG94-C9894EBXUQ)
10 | - [200316-IDEA + maven零基础构建java agent项目](https://blog.hhui.top/hexblog/2020/03/16/200316-IDEA-maven%E9%9B%B6%E5%9F%BA%E7%A1%80%E6%9E%84%E5%BB%BAjava-agent%E9%A1%B9%E7%9B%AE/)
11 | - [200316-手把手教你实现一个方法耗时统计的java agent](https://blog.hhui.top/hexblog/2020/03/16/200316-%E6%89%8B%E6%8A%8A%E6%89%8B%E6%95%99%E4%BD%A0%E5%AE%9E%E7%8E%B0%E4%B8%80%E4%B9%AA%E6%96%B9%E6%B3%95%E8%80%97%E6%97%B6%E7%BB%9F%E8%AE%A1%E7%9A%84java-agent/)
12 |
13 |
14 |
--------------------------------------------------------------------------------
/simple-agent/build.sh:
--------------------------------------------------------------------------------
1 | mvn assembly:assembly
2 |
--------------------------------------------------------------------------------
/simple-agent/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | java-agent
7 | com.git.hui.demo
8 | 1.0-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | simple-agent
13 |
14 |
15 |
16 |
17 | org.apache.maven.plugins
18 | maven-assembly-plugin
19 |
20 |
21 | jar-with-dependencies
22 |
23 |
24 |
25 | src/main/resources/META-INF/MANIFEST.MF
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | attached
40 |
41 | package
42 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/simple-agent/src/main/java/com/git/hui/agent/SimpleAgent.java:
--------------------------------------------------------------------------------
1 | package com.git.hui.agent;
2 |
3 | import java.lang.instrument.Instrumentation;
4 |
5 | /**
6 | * Created by @author yihui in 16:39 20/3/15.
7 | */
8 | public class SimpleAgent {
9 |
10 | /**
11 | * jvm 参数形式启动,运行此方法
12 | *
13 | * manifest需要配置属性Premain-Class
14 | *
15 | * @param agentArgs
16 | * @param inst
17 | */
18 | public static void premain(String agentArgs, Instrumentation inst) {
19 | System.out.println("premain");
20 | }
21 |
22 | /**
23 | * 动态 attach 方式启动,运行此方法
24 | *
25 | * manifest需要配置属性Agent-Class
26 | *
27 | * @param agentArgs
28 | * @param inst
29 | */
30 | public static void agentmain(String agentArgs, Instrumentation inst) {
31 | System.out.println("agentmain");
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/simple-agent/src/main/resources/META-INF/MANIFEST.MF:
--------------------------------------------------------------------------------
1 | Manifest-Version: 1.0
2 | Premain-Class: com.git.hui.agent.SimpleAgent
3 | Agent-Class: com.git.hui.agent.SimpleAgent
4 | Can-Redefine-Classes: true
5 | Can-Retransform-Classes: true
6 |
--------------------------------------------------------------------------------