背景
为了掌握链路中方法的执行情况,通过链路分析可以知道代码逻辑实现。
技术
显然,为了需要跟踪方法的执行情况,需要对方法进行aop拦截
生成字节码技术的方式有两种,一种类于javaproxy产生的字节码样式。一种是在进入方法和限出方法时注入代码实现aop代码增强(2)。具体实现见asm实现AOP的两篇文章
1.Asm实现静态AOP的两种方式-生成java-proxy类
2.Asm实现静态AOP的两种方式-在进入方法和限出方法时注入代码实现aop代码增强
另外,对于代码的跟踪,还需要一个数据结构来记录一个链路的代码执行情况。
具体实现
静态AOP基于java agent方式加载
java-javaagent:myagent.jar
基中myagent.jar就是实现链路方法跟踪的jar包。
链路跟踪类的实现
public class InvokeTree { static ThreadLocal<InvokeTree> localTree = new ThreadLocal<InvokeTree>(); public static class InvokeNode { public InvokeNode(int deep) { this.deep = deep; this.invokeCount = 1; } public InvokeNode parentNode; public List<InvokeNode> childNodes; public String invokeMethod; public int deep; public int invokeCount public boolean equals(InvokeNode other) { if (other == null) { return false; } if (!invokeMethod.equals(other.invokeMethod)) { return false; } if (childNodes == null && other.childNodes == null) { return true; } if (childNodes == null || other.childNodes == null) { return false; } int size = childNodes.size(); if (size != other.childNodes.size()) { return false; } for (int i = 0; i < size; i++) { InvokeNode x = childNodes.get(i); InvokeNode y = other.childNodes.get(i); if (!x.equals(y)) { return false; } } return true; } } public static void start(String invokeMethod) { ClientConfig clientConfig = Doom.getClientConfig(); if (!clientConfig.isOpenInvokeTreeAnalyse()) { return; } InvokeTree tree = new InvokeTree(); InvokeNode rootNode = new InvokeNode(0); rootNode.invokeMethod = invokeMethod; tree.rootNode = rootNode; tree.curNode = rootNode; localTree.set(tree); } public static void exit() { InvokeTree tree = localTree.get(); if (tree != null) { tree.curNode = tree.curNode.parentNode; } } public static void enter(String invokeMethod) { InvokeTree tree = localTree.get(); if (tree == null) { return; } InvokeNode parentNode = tree.curNode; InvokeNode newNode = new InvokeNode(parentNode.deep + 1); newNode.invokeMethod = invokeMethod; newNode.parentNode = parentNode; if (parentNode.childNodes == null) { parentNode.childNodes = new ArrayList<InvokeNode>(); } parentNode.childNodes.add(newNode); tree.curNode = newNode; //重复调用整理 cleanRepeatNode(parentNode); } public static void cleanRepeatNode(InvokeNode parentNode) { if (parentNode == null) { return; } int len = parentNode.childNodes.size(); if (len <= 1) { cleanRepeatNode(parentNode.parentNode); return; } InvokeNode a = parentNode.childNodes.get(len - 2); InvokeNode b = parentNode.childNodes.get(len - 1); if (a.equals(b)) { parentNode.childNodes.remove(len - 1); a.invokeCount++; } cleanRepeatNode(parentNode.parentNode); } public static void clear() { localTree.set(null); } public InvokeNode rootNode; public InvokeNode curNode; public static InvokeTree getCurrentTree() { return localTree.get(); } public String toString() { if (rootNode == null) { rootNode = curNode; } if (rootNode == null) { return "Empty Tree"; } else { StringBuilder sb = new StringBuilder(); buildShow(rootNode, "", sb, true); return sb.toString(); } } private void buildShow(InvokeNode node, String space, StringBuilder sb, boolean isParentLastNode) { if (node != null) { sb.append(space); if (node.parentNode != null) { sb.append("|-"); } sb.append(node.invokeMethod).append(node.invokeCount > 1 ? ("[repeat@" + node.invokeCount) + "]\n" : "\n"); if (node.deep <= 8) { if (node.childNodes != null && node.childNodes.size() > 0) { for (int i = 0; i < node.childNodes.size(); i++) { InvokeNode chNode = node.childNodes.get(i); buildShow(chNode, space + ((node.parentNode != null && isParentLastNode) ? "| " : " "), sb, (i != node.childNodes.size() - 1)); } } } } } public static void main(String[] args) { ClientConfig clientConfig = new ClientConfig(); clientConfig.setOpenInvokeTreeAnalyse(true); BootConfig.clientConfig = clientConfig; InvokeTree.start("test"); InvokeTree.enter("hello"); InvokeTree.enter("invoke1"); InvokeTree.enter("invokeSub1"); InvokeTree.exit(); InvokeTree.enter("invokeSub2"); InvokeTree.exit(); InvokeTree.enter("invokeSub2"); InvokeTree.exit(); InvokeTree.exit(); InvokeTree.enter("invoke2"); InvokeTree.enter("invoke21"); InvokeTree.exit(); InvokeTree.exit(); InvokeTree.enter("invoke2"); InvokeTree.enter("invoke21"); InvokeTree.exit(); InvokeTree.exit(); InvokeTree.exit(); InvokeTree.exit(); System.out.println(InvokeTree.getCurrentTree().toString()); } }
最后结合原生刚刚实现的AOP拦截
public class AutoMocker implements AsmMocker {
private String name;
@Override
public String getClassName() {
return name;
}
public AutoMocker(String name) {
this.name = name;
}
@Override
public List<String> getMethodPatterns() {
//拦截所有方法
List<String> patterns = new ArrayList<String>(); patterns.add("* *(*)"); return patterns; } @Override public InvocationInterceptor getInterceptor() { return new InvocationInterceptor() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //获取方法名 String invokeMethod = InvokeBuilder.buildIdentity(method.getDeclaringClass(), method); InvokeTree.enter(invokeMethod); try { return AsmInjector.invoke(proxy, method, args); } finally { InvokeTree.exit(); } } }; } @Override public boolean isIgnorePrivate() { return true; } @Override public boolean isIgnoreProtected() { return false; } @Override public List<String> getExcludePatterns() { return null; } }
//生成相关AOP代码并加载到内存中。
AutoMocker autoMocker = new AutoMocker(className);
//对所拦截的类生成AOP增强字节码 byte[] classBytes = AopUtils.buildMockedByeWithAsmMocker(autoMocker, classLoader, bytes);