导出 java 类之间的引用关系?

原始需求:
老大让我统计一下 大家修改频率较高的公共类,比如一些常量定义类

目前进展:

  1. 网上搜索到了jdeps, 但是不太会驾驭。查了几个类,代码里import的类数量比打印出来的要多,不明白为啥会缺
  2. 通过jdeps导出的 dot 文件,转成图之后,中间的箭头一大片都是黑的(因为被人引用太多了)…… 无法根据箭头连线看清具体的关系,不方便跟老大对线
  3. 尝试对 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

回到顶部