畅远数码|先要对微服务进行度量,进行微服务治理( 三 )


49.SingleVariableDeclarationsVar=mParams.get(i);
50.System.out.println("方法变量:"+sVar.getType().toString()+""+sVar.getName().toString());
51.}
52.}
53.//遍历方法内逻辑块
54.Blockbody=method.getBody();
55.if(body==null){
56.continue;
57.}
58.Liststatements=body.statements();
59.Iteratoriter=statements.iterator();
60.while(iter.hasNext()){
61.Statementstmt=(Statement)iter.next();
62.System.out.println("逻辑块类型:"+stmt.getClass().getSimpleName());
63.}
64.}
通过JDT-AST可以解析出某个类所有引用的其他类(import)列表、类变量列表、类函数列表、函数内变量列表和函数内逻辑块 。 有了这些基础信息之后 , 再遍历每个方法中的每一行 , 通过正则表达式可以获取此代码行所调用的变量及其方法 。 比如 , 针对下面的代码:
params.put("isAdded" , remind.getIsAdded());
通过正则表达式:
[a-zA-Z0-9_$]+[|r|n]*.[|r|n]*[a-zA-Z0-9_$]+(
可以识别出如下两个子串:
1.params.put(
2.remind.getIsAdded(
对上面的结果稍加处理 , 可知上述代码分别调用了变量params的put方法和变量remind的getIsAdded方法 。 基于这个结论 , 再根据类变量列表及函数内变量列表匹配到对应的类上 , 即可获得某个类方法调用其他类方法的情况 。 微服务本身即以类方法(或接口)的形式存在 , 因此 , 通过这种方式可以获得微服务之间的调用关系 , 具体解析过程如图2.12所示 。
有了这些信息 , 就可以逐个遍历方法 , 扫描方法的每一行代码 , 通过前面识别出的类变量及方法变量 , 找出这些变量的对外调用 , 从而构建出某个类方法对其他类方法的调用关系 。 如果把源码库中所有微服务工程的源码都进行扫描 , 可以获得一个Map对象集合 , Map的key是某个类方法 , Value是其调用的其他类方法的集合(为了程序处理方便 , 可能还需要构建一个类似的被调用关系集合Map) 。 在此基础上对这个Map进行递归遍历 , 就可以找出所有这些类方法的调用链路关系 , 如图2.13所示 。 图中的F#Func1和K#Func1是微服务的调用入口 , 一般都作为调用契约以接口的形式存在 。 在进行代码扫描时 , 要注意将其与实现类做关联(接口和实现类的关联关系可以通过AST获得 。
把如图2.13所示的这些调用链路关系合并 , 可以构建一个如图2.11右边所示的完整的方法级别的调用矩阵 , 微服务间的调用是这个调用矩阵的一个子集 。
图2.14是一个真实静态调用链的示例 , 以一个类方法为起点 , 找到它调用的所有其他方法 , 逐层遍历后 , 就能得到图中所显示的调用层级关系 。 图2.14-①是这种调用关系的文本描述 , 从图中可以清晰地看到方法间调用的先后和层级关系 。
要注意的是类的实现和继承关系 。 接口类方法或者抽象类方法是没有具体实现逻辑的 , 所以在程序扫描时 , 还需维护类直接的继承和实现关系 。 接口方法往往用具体的实现类方法来代替 , 这样就能顺利地找到它的下一层引用关系 。
如果引入诸如mxGraph这类图形化展示组件 , 可以将图2.14-①中类方法间的调用关系用一棵从上至下、从左至右的调用树图来展示 , 如图2.14-②所示 。 调用树上每一个节点就是一个类方法 , 节点间的箭头连线就是一个调用关系 。 通过JDT能够识别出方法注释 , 还可以将方法注释在每一个类方法节点的右边列出 。 如果系统注释完整 , 那么通过一张图就可以基本读清楚一个微服务入口方法的完整实现细节 。


推荐阅读