在大型项目中,如何快速查找其中的调用关系,快速的定位问题,了解业务之间的关系可以使用 Idea 中的 Method Hierarchy 功能,可以显示其调用关系,其快捷键的方式为 Control+ alt + H
。
通过点击如图的
Export to Text File
,可以看到其中的调用关系。展示如下图所示:
排除Test的方法之后,如下图所示:
CachingExecutor.query(MappedStatement, Object, RowBounds, ResultHandler) (org.apache.ibatis.executor)
SelectKeyGenerator.processGeneratedKeys(Executor, MappedStatement, Object) (org.apache.ibatis.executor.keygen)
DefaultSqlSession.select(String, Object, RowBounds, ResultHandler) (org.apache.ibatis.session.defaults)
DefaultSqlSession.select(String, Object, ResultHandler) (org.apache.ibatis.session.defaults)
MapperMethod.executeWithResultHandler(SqlSession, Object[]) (org.apache.ibatis.binding)
MapperMethod.execute(SqlSession, Object[]) (org.apache.ibatis.binding)
PlainMethodInvoker in MapperProxy.invoke(Object, Method, Object[], SqlSession) (org.apache.ibatis.binding)
MapperProxy.invoke(Object, Method, Object[]) (org.apache.ibatis.binding)
StubInvocationHandlerImpl.invoke(Object, Method, Object[])(2 usages) (com.sun.corba.se.impl.presentation.rmi)
CompositeInvocationHandlerImpl.invoke(Object, Method, Object[]) (com.sun.corba.se.spi.orbutil.proxy)
PooledConnection.invoke(Object, Method, Object[]) (org.apache.ibatis.datasource.pooled)
NestedResultHandlerAssociationTest.shouldHandleStop() (org.apache.ibatis.submitted.nestedresulthandler_association)
SqlSessionTest.shouldStopResultHandler() (org.apache.ibatis.session)
SqlSessionManager.select(String, Object, ResultHandler) (org.apache.ibatis.session)
MapperMethod.executeWithResultHandler(SqlSession, Object[]) (org.apache.ibatis.binding)
MapperMethod.execute(SqlSession, Object[]) (org.apache.ibatis.binding)
PlainMethodInvoker in MapperProxy.invoke(Object, Method, Object[], SqlSession) (org.apache.ibatis.binding)
MapperProxy.invoke(Object, Method, Object[]) (org.apache.ibatis.binding)
StubInvocationHandlerImpl.invoke(Object, Method, Object[])(2 usages) (com.sun.corba.se.impl.presentation.rmi)
PooledConnection.invoke(Object, Method, Object[]) (org.apache.ibatis.datasource.pooled)
CompositeInvocationHandlerImpl.invoke(Object, Method, Object[]) (com.sun.corba.se.spi.orbutil.proxy)
NestedResultHandlerAssociationTest.shouldHandleStop() (org.apache.ibatis.submitted.nestedresulthandler_association)
SqlSessionTest.shouldStopResultHandler() (org.apache.ibatis.session)
SqlSessionManager.select(String, Object, ResultHandler) (org.apache.ibatis.session)
MapperMethod.executeWithResultHandler(SqlSession, Object[]) (org.apache.ibatis.binding)
NestedResultHandlerAssociationTest.shouldHandleRowBounds() (org.apache.ibatis.submitted.nestedresulthandler_association)
SqlSessionManager.select(String, Object, RowBounds, ResultHandler) (org.apache.ibatis.session)
DefaultSqlSession.select(String, ResultHandler) (org.apache.ibatis.session.defaults)
DefaultSqlSession.selectList(String, Object, RowBounds) (org.apache.ibatis.session.defaults)
DefaultSqlSession.selectList(String, Object) (org.apache.ibatis.session.defaults)
DefaultSqlSession.selectList(String) (org.apache.ibatis.session.defaults)
DefaultSqlSession.selectOne(String, Object) (org.apache.ibatis.session.defaults)
MapperMethod.executeForMany(SqlSession, Object[]) (org.apache.ibatis.binding)
SqlSessionManager.selectList(String, Object) (org.apache.ibatis.session)
SqlSessionManager.selectList(String, Object, RowBounds) (org.apache.ibatis.session)
DefaultSqlSession.selectMap(String, Object, String, RowBounds) (org.apache.ibatis.session.defaults)
SqlSessionManager.selectMap(String, Object, String, RowBounds) (org.apache.ibatis.session)
SqlSessionManager.selectMap(String, Object, String, RowBounds) (org.apache.ibatis.session)
SqlSessionManager.selectMap(String, Object, String, RowBounds) (org.apache.ibatis.session)
SqlSessionManager.selectMap(String, Object, String, RowBounds) (org.apache.ibatis.session)
SqlSessionManager.selectMap(String, Object, String, RowBounds) (org.apache.ibatis.session)
MapperMethod.executeForMap(SqlSession, Object[]) (org.apache.ibatis.binding)
MapperMethod.executeForMap(SqlSession, Object[]) (org.apache.ibatis.binding)
MapperMethod.executeForMap(SqlSession, Object[]) (org.apache.ibatis.binding)
MapperMethod.executeForMap(SqlSession, Object[]) (org.apache.ibatis.binding)
MapperMethod.execute(SqlSession, Object[]) (org.apache.ibatis.binding)
DefaultSqlSession.selectMap(String, String) (org.apache.ibatis.session.defaults)
DefaultSqlSession.selectMap(String, Object, String) (org.apache.ibatis.session.defaults)
MapperMethod.executeForMap(SqlSession, Object[]) (org.apache.ibatis.binding)
MapperMethod.executeForMany(SqlSession, Object[]) (org.apache.ibatis.binding)
这里展示的是方法的调用关系,自底向顶展示其调用,仔细观察一下,这里的调用层级是一个树形结构,因此采用树形结构来存储其关系,然后采用深度优先的算法打印自底向顶的调用链。 定义的树形节点,这里的树不是二叉树,而是多叉树。
// 树形结构节点
@Data
public class TNode {
// 层级
private int level;
// 类名
private String klass;
// 方法
private String method;
// 包名
private String pack;
// 原始内容
private String origin;
// 子节点
private List<TNode> childList;
// 添加子节点
public void addChild(TNode node) {
if (CollUtil.isEmpty(childList)) {
childList = new ArrayList<>();
}
childList.add(node);
}
}
解析成树形结构然后进行打印
// 分割符
private static final String BL = " ";
public static void main(String[] args) {
String text = "";
analyzeTNode(text);
}
// 解析其节点数据
private static TNode analyzeTNode(String text) {
List<String> lst = new ArrayList<>();
int arrLen = 0;
for (String node : text.split("\n")) {
if (node.contains("Anonymous in ")) {
continue;
}
String name = node.replaceAll("\(.*\)", "");
// 空格都替换为*,以此来标记其层级
String tmp = name.replace(BL, "*");
lst.add(tmp);
int count = StrUtil.count(tmp, "*");
if (count >= arrLen) {
arrLen = count;
}
}
TNode[] levelList = new TNode[arrLen + 1];
String root = lst.get(0);
// 根节点
TNode tNode = formatNode(0, root);
levelList[0] = tNode;
for (int i = 1, len = lst.size(); i < len; i++) {
String tmp = lst.get(i);
int count = StrUtil.count(tmp, "*");
String node = tmp.replace("*", "");
TNode tmpNode = formatNode(count, node);
TNode tNode1 = levelList[count - 1];
levelList[count] = tmpNode;
tNode1.addChild(tmpNode);
}
// 遍历打印节点
printNode(tNode, arrLen);
return tNode;
}
/**
* 组装节点信息
*/
private static TNode formatNode(int count, String node) {
TNode tmpNode = new TNode();
tmpNode.setOrigin(node);
tmpNode.setLevel(count);
tmpNode.setMethod(node.split("\.")[1]);
tmpNode.setKlass(node.split("\.")[0]);
return tmpNode;
}
// 深度遍历数据节点
public static void printNode(TNode node, int arrLen) {
String[] levelList = new String[arrLen + 1];
Set<TNode> nodeSet = new HashSet<>();
LinkedList<TNode> stack = new LinkedList<>();
nodeSet.add(node);
stack.add(node);
while (CollUtil.isNotEmpty(stack)) {
TNode cur = stack.pop();
levelList[cur.getLevel()] = cur.getOrigin();
if (CollUtil.isEmpty(cur.getChildList())) {
printInvoke(levelList, cur.getLevel());
continue;
}
for (TNode next : cur.getChildList()) {
if (!nodeSet.contains(next)) {
stack.push(cur);
stack.push(next);
nodeSet.add(next);
break;
}
}
}
}
// 打印节点
private static void printInvoke(String[] levelList, int level) {
StringBuffer buffer = new StringBuffer();
for (int num = 0; num <= level; num++) {
// 使用 \t 进行分割数据
buffer.append(levelList[num]).append("\t");
}
System.out.println(buffer.toString());
}
最终打印其结果:从左到右的层级依次递增
CachingExecutor.query SelectKeyGenerator.processGeneratedKeys
CachingExecutor.query DefaultSqlSession.select DefaultSqlSession.select MapperMethod.executeWithResultHandler MapperMethod.execute PlainMethodInvoker in MapperProxy.invoke MapperProxy.invoke StubInvocationHandlerImpl.invoke
CachingExecutor.query DefaultSqlSession.select DefaultSqlSession.select MapperMethod.executeWithResultHandler MapperMethod.execute PlainMethodInvoker in MapperProxy.invoke MapperProxy.invoke CompositeInvocationHandlerImpl.invoke
CachingExecutor.query DefaultSqlSession.select DefaultSqlSession.select MapperMethod.executeWithResultHandler MapperMethod.execute PlainMethodInvoker in MapperProxy.invoke MapperProxy.invoke PooledConnection.invoke
CachingExecutor.query DefaultSqlSession.select DefaultSqlSession.select NestedResultHandlerAssociationTest.shouldHandleStop
CachingExecutor.query DefaultSqlSession.select DefaultSqlSession.select SqlSessionTest.shouldStopResultHandler
CachingExecutor.query DefaultSqlSession.select DefaultSqlSession.select SqlSessionManager.select MapperMethod.executeWithResultHandler MapperMethod.execute PlainMethodInvoker in MapperProxy.invoke MapperProxy.invoke StubInvocationHandlerImpl.invoke
CachingExecutor.query DefaultSqlSession.select DefaultSqlSession.select SqlSessionManager.select MapperMethod.executeWithResultHandler MapperMethod.execute PlainMethodInvoker in MapperProxy.invoke MapperProxy.invoke PooledConnection.invoke
CachingExecutor.query DefaultSqlSession.select DefaultSqlSession.select SqlSessionManager.select MapperMethod.executeWithResultHandler MapperMethod.execute PlainMethodInvoker in MapperProxy.invoke MapperProxy.invoke CompositeInvocationHandlerImpl.invoke
CachingExecutor.query DefaultSqlSession.select DefaultSqlSession.select SqlSessionManager.select NestedResultHandlerAssociationTest.shouldHandleStop
CachingExecutor.query DefaultSqlSession.select DefaultSqlSession.select SqlSessionManager.select SqlSessionTest.shouldStopResultHandler
CachingExecutor.query DefaultSqlSession.select DefaultSqlSession.select SqlSessionManager.select SqlSessionManager.select
CachingExecutor.query DefaultSqlSession.select MapperMethod.executeWithResultHandler
CachingExecutor.query DefaultSqlSession.select NestedResultHandlerAssociationTest.shouldHandleRowBounds
CachingExecutor.query DefaultSqlSession.select SqlSessionManager.select
CachingExecutor.query DefaultSqlSession.select DefaultSqlSession.select
CachingExecutor.query DefaultSqlSession.selectList DefaultSqlSession.selectList DefaultSqlSession.selectList
CachingExecutor.query DefaultSqlSession.selectList DefaultSqlSession.selectList DefaultSqlSession.selectOne
CachingExecutor.query DefaultSqlSession.selectList DefaultSqlSession.selectList MapperMethod.executeForMany
CachingExecutor.query DefaultSqlSession.selectList DefaultSqlSession.selectList SqlSessionManager.selectList
CachingExecutor.query DefaultSqlSession.selectList SqlSessionManager.selectList
CachingExecutor.query DefaultSqlSession.selectList DefaultSqlSession.selectMap SqlSessionManager.selectMap SqlSessionManager.selectMap SqlSessionManager.selectMap SqlSessionManager.selectMap SqlSessionManager.selectMap
CachingExecutor.query DefaultSqlSession.selectList DefaultSqlSession.selectMap SqlSessionManager.selectMap SqlSessionManager.selectMap SqlSessionManager.selectMap SqlSessionManager.selectMap MapperMethod.executeForMap
CachingExecutor.query DefaultSqlSession.selectList DefaultSqlSession.selectMap SqlSessionManager.selectMap SqlSessionManager.selectMap SqlSessionManager.selectMap MapperMethod.executeForMap
CachingExecutor.query DefaultSqlSession.selectList DefaultSqlSession.selectMap SqlSessionManager.selectMap SqlSessionManager.selectMap MapperMethod.executeForMap
CachingExecutor.query DefaultSqlSession.selectList DefaultSqlSession.selectMap SqlSessionManager.selectMap MapperMethod.executeForMap MapperMethod.execute
CachingExecutor.query DefaultSqlSession.selectList DefaultSqlSession.selectMap DefaultSqlSession.selectMap
CachingExecutor.query DefaultSqlSession.selectList DefaultSqlSession.selectMap MapperMethod.executeForMap
CachingExecutor.query DefaultSqlSession.selectList MapperMethod.executeForMany