我们知道,简单的实现一个java agent是实现如下接口
public static void premain(String agentArgs, Instrumentation inst); //【1】
public static void premain(String agentArgs); //【2】
其中,【1】和【2】同时存在时,【1】会优先被执行,而【2】则会被忽略。
这两个都适合在应用启动的时候加载,我们也可以提供一种动态挂载的方式,代码如下
public static void agentmain(String agentArgs, Instrumentation inst){
System.out.println("load agent...");
inst.addTransformer(new AgentClazzTransformer(),true);
}
基于此,我们把agent的基本项目结构搭建起来
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>jvm_sandbox_demo</artifactId>
<groupId>com.deer</groupId>
<version>1.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>com.deer.agent</groupId>
<artifactId>jvm_sandbox_agent</artifactId>
<name>jvm_sandbox_agent</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.5.5</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifestEntries>
<Premain-Class>com.deer.agent.AgentMain</Premain-Class>
<Agent-Class>com.deer.agent.AgentMain</Agent-Class>
<Can-Redefine-Classes>true</Can-Redefine-Classes>
<Can-Retransform-Classes>true</Can-Retransform-Classes>
</manifestEntries>
</archive>
</configuration>
<executions>
<execution>
<goals>
<goal>attached</goal>
</goals>
<phase>package</phase>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
</plugins>
</build>
</project>
package com.deer.agent;
import com.deer.agent.sandbox.AgentClazzTransformer;
import java.lang.instrument.Instrumentation;
public class AgentMain {
public static void premain(String agentArgs, Instrumentation inst) {
System.out.println("premain");
}
public static void agentmain(String agentArgs, Instrumentation inst){
System.out.println("load agent...");
inst.addTransformer(new AgentClazzTransformer(),true);
}
}
package com.deer.agent.sandbox;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
public class AgentClazzTransformer implements ClassFileTransformer {
@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
if(className!=null && className.startsWith("com/deer/base/service")){
System.out.println("I can see you ...."+className);
}
return classfileBuffer;
}
}
基于此,我们把要测试的代码稍微修改一下
package com.deer.base.controller;
import com.deer.base.service.BaseService;
import com.deer.base.service.HelloService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@Autowired
private HelloService helloService;
@RequestMapping("/sayHi/{userName}")
public String sayHello(@PathVariable(name ="userName") String userName) {
//新增加的代码
BaseService.record(userName);
return helloService.sayHello(userName);
}
}
package com.deer.base.service;
public class BaseService {
public static void record(String value){
System.out.println("one record:"+value);
}
}