导出 java 类之间的引用关系?
原始需求:
老大让我统计一下 大家修改频率较高的公共类,比如一些常量定义类
目前进展:
- 网上搜索到了jdeps, 但是不太会驾驭。查了几个类,代码里import的类数量比打印出来的要多,不明白为啥会缺
- 通过jdeps导出的 dot 文件,转成图之后,中间的箭头一大片都是黑的(因为被人引用太多了)…… 无法根据箭头连线看清具体的关系,不方便跟老大对线
- 尝试对 jdeps导出的内容直接进行过滤 + 排序 + 统计出现次数,希望通过这个方式查看类被引用的次数。但是跟预期的结果有偏差
求助一下大佬们有没有好的解题思路 谢谢各位
回答:
你已经提到查看提交记录来统计修改频率。
至于使用频率,这个肯定要分析整个项目里每一个java文件,统计每个类中字段和方法的使用次数。
你可以分析java文件,构建抽象语法树,然后分析语法树中每一个节点,梳理出调用关系啥的,不过这应该也是个大工程。这个我看到有个java库:javaparser。
一个使用javaparser打印分析结果的例子:
public class Test { public static void main(String[] args) {
// Parse the code you want to inspect:
CompilationUnit cu = StaticJavaParser.parse("import java.lang.String; class X { public void abc(){String abe;abe.toString();} }");
YamlPrinter printer = new YamlPrinter(true);
System.out.println(printer.output(cu));
}
}
或者找现成的工具,比如idea里边,你在代码的字段,方法,变量上右键,有个find usage功能,可以查看字段和方法的引用情况,这个应该就是你想要的,你可以了解一下idea的插件,看有没有提供获取find usage的接口。
jadx中也有类似功能,代码应该是jadx.gui.ui.UsageDialog#performSearch
使用javaparser好像就可以完成,我这个在accessMap中保存了字段和方法被引用的次数。
需要项目源码目录,以及依赖的所有外部jar包目录,外部jar可以先用maven打包,把所有jar整合到一起。
import com.github.javaparser.ParseResult;import com.github.javaparser.ParserConfiguration;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.expr.FieldAccessExpr;
import com.github.javaparser.ast.expr.MethodCallExpr;
import com.github.javaparser.ast.expr.NameExpr;
import com.github.javaparser.resolution.declarations.ResolvedEnumConstantDeclaration;
import com.github.javaparser.resolution.declarations.ResolvedFieldDeclaration;
import com.github.javaparser.resolution.declarations.ResolvedValueDeclaration;
import com.github.javaparser.resolution.types.ResolvedType;
import com.github.javaparser.symbolsolver.JavaSymbolSolver;
import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserParameterDeclaration;
import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserVariableDeclaration;
import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
import com.github.javaparser.symbolsolver.model.typesystem.ReferenceTypeImpl;
import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSolver;
import com.github.javaparser.symbolsolver.resolution.typesolvers.JarTypeSolver;
import com.github.javaparser.symbolsolver.resolution.typesolvers.JavaParserTypeSolver;
import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver;
import com.github.javaparser.utils.SourceRoot;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
public class ParseProject {
public static void main(String[] args) throws IOException {
Path projectRoot = Paths.get("G:\\kaifa_environment\\code\\java\\java-agent");
String[] roots = new String[]{
"java-agent-main\\src\\main\\java",
"java-agent-test\\src\\main\\java",
/* "java-parse-main\\src\\main\\java",*/
};
Map<String, Integer> accessMap = new HashMap();
for (String root : roots) {
Path codeSourceRoot = projectRoot.resolve(root);
System.out.println(codeSourceRoot);
SourceRoot sourceRoot = new SourceRoot(codeSourceRoot);
//项目依赖的所有外部jar路径,以及源码路径
TypeSolver myTypeSolver = new CombinedTypeSolver(
new ReflectionTypeSolver(),
new JarTypeSolver("D:\\kaifa_environment\\maven-repository\\org\\javassist\\javassist\\3.28.0-GA\\javassist-3.28.0-GA.jar")
, new JarTypeSolver("D:\\kaifa_environment\\maven-repository\\org\\benf\\cfr\\0.152\\cfr-0.152.jar")
, new JavaParserTypeSolver(codeSourceRoot)
);
JavaSymbolSolver symbolSolver = new JavaSymbolSolver(myTypeSolver);
ParserConfiguration parserConfiguration = new ParserConfiguration();
parserConfiguration.setSymbolResolver(symbolSolver);
try {
sourceRoot.getParserConfiguration().setSymbolResolver(symbolSolver);
List<ParseResult<CompilationUnit>> parseResults = sourceRoot.tryToParse();
for (ParseResult<CompilationUnit> parseResult : parseResults) {
parseResult.ifSuccessful(cu -> {
List<String> methodCall = getMethodCall(cu);
// System.out.println("方法:");
// System.out.println(methodCall);
List<String> fieldCall = getFieldCall(cu);
// System.out.println("字段:");
// System.out.println(fieldCall);
methodCall.addAll(fieldCall);
for (String qualifiedName : methodCall) {
Integer count = accessMap.getOrDefault(qualifiedName, Integer.valueOf(0));
accessMap.put(qualifiedName, count + 1);
}
});
}
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println(accessMap.size());
}
public static List<String> getMethodCall(CompilationUnit cu) {
List<String> methodCallExprs = new ArrayList<>();
cu.findAll(MethodCallExpr.class).forEach(mce -> {
methodCallExprs.add(mce.resolve().getQualifiedSignature());
});
return methodCallExprs;
}
public static List<String> getFieldCall(CompilationUnit cu) {
List<String> fieldAccess = new ArrayList<>();
//获取字段访问情况
cu.findAll(FieldAccessExpr.class).forEach(new Consumer<FieldAccessExpr>() {
@Override
public void accept(FieldAccessExpr mce) {
try {
ResolvedValueDeclaration resolve = mce.resolve();
String fieldPath = getQualifiedName(resolve, mce.getNameAsString());
fieldAccess.add(fieldPath);
} catch (Exception e) {
}
}
});
// 名称访问情况
cu.findAll(NameExpr.class).forEach(mce -> {
try {
ResolvedValueDeclaration resolve = mce.resolve();
//System.out.println(mce.getName());
String fieldPath = getQualifiedName(resolve, mce.getNameAsString());
if (fieldPath.length() > 0) {
fieldAccess.add(fieldPath);
}
} catch (Exception e) {
}
});
return fieldAccess;
}
public static String getQualifiedName(ResolvedValueDeclaration resolve, String fieldName) {
String fieldPath = "";
/* if (resolve instanceof JavaParserFieldDeclaration) {
JavaParserFieldDeclaration javaParserFieldDeclaration = (JavaParserFieldDeclaration) resolve;
FieldDeclaration wrappedNode = javaParserFieldDeclaration.getWrappedNode();
Node parentNode = wrappedNode.getParentNode().get();
if (parentNode instanceof ClassOrInterfaceDeclaration) {
ClassOrInterfaceDeclaration typeDeclaration = (ClassOrInterfaceDeclaration) parentNode;
//获取typeDeclaration的qualifiedName
fieldPath = typeDeclaration.getFullyQualifiedName().get() + "." + fieldName;
}
} else if (resolve instanceof ReflectionFieldDeclaration) {
ReflectionFieldDeclaration reflectionFieldDeclaration = (ReflectionFieldDeclaration) resolve;
ResolvedTypeDeclaration resolvedTypeDeclaration = reflectionFieldDeclaration.declaringType();//获取字段的类型
fieldPath = resolvedTypeDeclaration.getQualifiedName() + "." + fieldName;
} else if (resolve instanceof JavassistFieldDeclaration){
ResolvedFieldDeclaration javassistFieldDeclaration = (ResolvedFieldDeclaration) resolve;
fieldPath = javassistFieldDeclaration.declaringType().getQualifiedName() + "." + fieldName;
}*/
if (resolve instanceof ResolvedFieldDeclaration) {
ResolvedFieldDeclaration resolvedValueDeclaration = (ResolvedFieldDeclaration) resolve;
fieldPath = resolvedValueDeclaration.declaringType().getQualifiedName() + "." + fieldName;
} else if (resolve instanceof ResolvedEnumConstantDeclaration) {
ResolvedType type = resolve.getType();
String qualifiedName = ((ReferenceTypeImpl) type).getTypeDeclaration().get().getQualifiedName();
fieldPath = qualifiedName + "." + fieldName;
} else if (resolve instanceof JavaParserParameterDeclaration) {
//System.out.println("函数入参:"+resolve.getName());
} else if (resolve instanceof JavaParserVariableDeclaration) {
//System.out.println("变量引用:"+resolve.getName());
} else {
System.out.println("没解析的类型");
}
return fieldPath;
}
}
以上是 导出 java 类之间的引用关系? 的全部内容, 来源链接: utcz.com/p/944386.html