前言
java agent即为java.lang.instrument。官方解释

可以理解为字节码增强技术。一般结合字节码修改框架,比如Javassist,ASM(门槛比较高)等框架一起使用。比如pinpoint,skywalking,arthas等做全链路监控或者服务分析。
技术原理
参考你假笨大佬的图

写个DEMO
1,首先引入javassist字节码编辑包工具
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
</dependency>
2,编写一个待注入服务,并打包成jar包
//--接口
public class JobService {
public void exec(String params) {
System.out.println("JobService start job.....");
System.out.println("job params: " + params + ".");
System.out.println("JobService end job.....");
}
}
//--调用接口主函数
public class JavaAgentMainTest {
public static void main(String[] args) {
test1();
}
private static void test1() {
JobService jobService = new JobService();
jobService.exec("a=1;b=2;");
}
}
3,编译打包成jar

注意第4步需要在 src/main/resources/目录下生成MANIFEST.MF文件,记得在Main-Class后面需要有一个空行。

打包

4,检测jar包是否打包正常
java -jar examples.jar

5,编写javaagent插件jar包
给JobService.exec添加耗时统计的代码
#字节码编辑类
public class TimeConsumerTransformer implements ClassFileTransformer {
final static String startTime = "\nlong startTime = System.nanoTime();\n";
final static String endTime = "\nlong endTime = System.nanoTime();\n";
final static Map<String, List<String>> methodMap = new HashMap<>();
public TimeConsumerTransformer() {
//--把代理的类全部加到待处理链表中
addMethod("com.whg.ex.deeplearnv2.examples.javaagents.JobService", "exec");
}
private TimeConsumerTransformer addMethod(String className, String methodName) {
List<String> list = methodMap.computeIfAbsent(className, cn -> new ArrayList<>());
list.add(methodName);
return this;
}
@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
className = className.replace("/", ".");
System.out.println("agent className:" + className);
if (methodMap.containsKey(className)) {
try {
CtClass ctClass = ClassPool.getDefault().get(className);
//--给对应对象的所有方法添加耗时统计
for (String methodName : methodMap.get(className)) {
String cost = "\nSystem.out.println(\" Method:"
+ methodName
+ " Cost:\"+(endTime-startTime)+"
+ "\"ns\");\n";
CtMethod ctMethod = ctClass.getDeclaredMethod(methodName);
String wrappedMethodName = methodName + "$wrapped";
ctMethod.setName(wrappedMethodName);
CtMethod newMethod = CtNewMethod.copy(ctMethod, methodName, ctClass, null);
StringBuilder newMethodBody = new StringBuilder();
newMethodBody.append("{");
newMethodBody.append(startTime);
newMethodBody.append(wrappedMethodName + "($$);\n");
newMethodBody.append(endTime);
newMethodBody.append(cost);
newMethodBody.append("}");
newMethod.setBody(newMethodBody.toString());
ctClass.addMethod(newMethod);
}
return ctClass.toBytecode();
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
}
#插件类
public class JavaTcAgentPlugins {
public static void premain(String agentArgs, Instrumentation instrumentation) {
System.out.println("JavaAgentTimeConsumer start");
System.out.println("JavaAgentTimeConsumer params:" + agentArgs);
instrumentation.addTransformer(new TimeConsumerTransformer());
System.out.println("JavaAgentTimeConsumer result");
}
public static void main(String[] args) {
//--为打包成jar作用
System.out.println("JavaTimeConsumerAgent test...");
}
}
6,按之前的方式打包jar

需要注意的是MANIFEST.MF需要修改为Premain-Class。

7,测试
执行java -javaagent:../examples_plugins_jar/examples-plugins.jar=whgtest -jar examples.jar

可以看到耗时统计的代码已经动态添加到原方法中了。
网友评论