一、原理:基于javaAgent和Java字节码注入技术的java探针工具技术原理html
二、原理分析java
动态代理功能实现说明,咱们利用javaAgent和ASM字节码技术开发java探针工具,实现原理以下:浏览器
jdk1.5之后引入了javaAgent技术,javaAgent是运行方法以前的拦截器。咱们利用javaAgent和ASM字节码技术,在JVM加载class二进制文件的时候,利用ASM动态的修改加载的class文件,在监控的方法先后添加计时器功能,用于计算监控方法耗时,同时将方法耗时及内部调用状况放入处理器,处理器利用栈先进后出的特色对方法调用前后顺序作处理,当一个请求处理结束后,将耗时方法轨迹和入参map输出到文件中,而后根据map中相应参数或耗时方法轨迹中的关键代码区分出咱们要抓取的耗时业务。最后将相应耗时轨迹文件取下来,转化为xml格式并进行解析,经过浏览器将代码分层结构展现出来,方便耗时分析,如图下图所示。app
Java探针工具功能点:ide
一、支持方法执行耗时范围抓取设置,根据耗时范围抓取系统运行时出如今设置耗时范围的代码运行轨迹。工具
二、支持抓取特定的代码配置,方便对配置的特定方法进行抓取,过滤出关系的代码执行耗时状况。post
三、支持APP层入口方法过滤,配置入口运行前的方法进行监控,至关于监控特有的方法耗时,进行方法专题分析。ui
四、支持入口方法参数输出功能,方便跟踪耗时高的时候对应的入参数。this
五、提供WEB页面展现接口耗时展现、代码调用关系图展现、方法耗时百分比展现、可疑方法凸显功能。命令行
三、实例:
JavaAgent 是JDK 1.5 之后引入的,也能够叫作Java代理。
JavaAgent 是运行在 main方法以前的拦截器,它内定的方法名叫 premain ,也就是说先执行 premain 方法而后再执行 main 方法。
查看原做者实例地址:https://www.cnblogs.com/aspirant/p/8796974.html
JavaAgent 的应用场景
JDK5中只能经过命令行参数在启动JVM时指定javaagent参数来设置代理类,而JDK6中已经不只限于在启动JVM时经过配置参数来设置代理类,JDK6中经过 Java Tool API 中的 attach 方式,咱们也能够很方便地在运行过程当中动态地设置加载代理类,以达到 instrumentation 的目的。
Instrumentation 的最大做用,就是类定义动态改变和操做。
最简单的一个例子,计算某个方法执行须要的时间,不修改源代码的方式,使用Instrumentation 代理来实现这个功能,给力的说,这种方式至关于在JVM级别作了AOP支持,这样咱们能够在不修改应用程序的基础上就作到了AOP。
package com.shanhy.demo.agent; import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.IllegalClassFormatException; import java.security.ProtectionDomain; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javassist.ClassPool; import javassist.CtClass; import javassist.CtMethod; import javassist.CtNewMethod; /** * 检测方法的执行时间 * */ public class MyTransformer implements ClassFileTransformer { final static String prefix = "\nlong startTime = System.currentTimeMillis();\n"; final static String postfix = "\nlong endTime = System.currentTimeMillis();\n"; // 被处理的方法列表 final static Map<String, List<String>> methodMap = new HashMap<String, List<String>>(); public MyTransformer() { add("com.shanhy.demo.TimeTest.sayHello"); add("com.shanhy.demo.TimeTest.sayHello2"); } private void add(String methodString) { String className = methodString.substring(0, methodString.lastIndexOf(".")); String methodName = methodString.substring(methodString.lastIndexOf(".") + 1); List<String> list = methodMap.get(className); if (list == null) { list = new ArrayList<String>(); methodMap.put(className, list); } list.add(methodName); } @Override public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { className = className.replace("/", "."); if (methodMap.containsKey(className)) {// 判断加载的class的包路径是否是须要监控的类 CtClass ctclass = null; try { ctclass = ClassPool.getDefault().get(className);// 使用全称,用于取得字节码类<使用javassist> for (String methodName : methodMap.get(className)) { String outputStr = "\nSystem.out.println(\"this method " + methodName + " cost:\" +(endTime - startTime) +\"ms.\");"; CtMethod ctmethod = ctclass.getDeclaredMethod(methodName);// 获得这方法实例 String newMethodName = methodName + "$old";// 新定义一个方法叫作好比sayHello$old ctmethod.setName(newMethodName);// 将原来的方法名字修改 // 建立新的方法,复制原来的方法,名字为原来的名字 CtMethod newMethod = CtNewMethod.copy(ctmethod, methodName, ctclass, null); // 构建新的方法体 StringBuilder bodyStr = new StringBuilder(); bodyStr.append("{"); bodyStr.append(prefix); bodyStr.append(newMethodName + "($$);\n");// 调用原有代码,相似于method();($$)表示全部的参数 bodyStr.append(postfix); bodyStr.append(outputStr); bodyStr.append("}"); newMethod.setBody(bodyStr.toString());// 替换新方法 ctclass.addMethod(newMethod);// 增长新方法 } return ctclass.toBytecode(); } catch (Exception e) { System.out.println(e.getMessage()); e.printStackTrace(); } } return null; } }