<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.2</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.4</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>19.0</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>com.github.javaparser</groupId>
<artifactId>javaparser-core</artifactId>
<version>3.25.9</version>
</dependency>
<dependency>
<groupId>fr.inria.gforge.spoon</groupId>
<artifactId>spoon-core</artifactId>
<version> 7.5.0</version>
</dependency>
将项目统一放在一个目录下,进行关联扫描
public class CodeAnalyzer {
static Map<String, List<String>> interfacesDubboMap = new HashMap<>();
static String searchFolder = "E:\\workspace4"; // 替换为你的搜索路径
public static void main(String[] args) throws Exception {
List<String> interfaces = FileUtils.readLines(new File("E:\\workspace4\\interface.txt"), "UTF-8");
for (String dubbo : interfaces) {
dubbo = dubbo.trim();
interfacesDubboMap.put(dubbo, Lists.newArrayList());
String searchText = dubbo; // 替换为你要搜索的具体文本
int indexOf = searchText.lastIndexOf(".");
String interfaceName = searchText.substring(0, indexOf);
String interfaceClassName = searchText.substring(indexOf + 1, searchText.length());
String methodName = interfaceClassName;
extracted(searchText, searchFolder, Lists.newArrayList(methodName), interfaceName, interfaceClassName);
}
String jsonString = new Gson().toJson(interfacesDubboMap);
System.out.println(jsonString);
}
/**
* 同package 的 方法调用
* @param absolutePath
* @param methodNames
* @param interfaceClassName
* @return
* @throws IOException
*/
private static List<String> findSamePackageClassPath(String absolutePath, List<String> methodNames, String interfaceClassName) throws IOException {
String samePackagePath = Paths.get(absolutePath).getParent().toString();
Set<String> path1s = Sets.newHashSet();
Set<String> duplicates = Sets.newHashSet();
for (String methodName : methodNames) {
List<String> pathsForClassName = findFileBySearchText(samePackagePath, interfaceClassName);
List<String> pathsForMethodName = findFileBySearchText(samePackagePath, methodName);
// pathsForClassName pathsForMethodName 找出同样的数据
path1s.addAll(pathsForClassName);
duplicates.addAll(pathsForMethodName);
}
duplicates.retainAll(path1s);
return Lists.newArrayList(duplicates);
}
private static void extracted(String searchText, String absolutePath, List<String> methodNames, String interfaceName, String interfaceClassName) throws Exception {
if (CollectionUtils.isEmpty(methodNames)) {
return;
}
List<String> allPath = Lists.newArrayList();
List<String> samePackageClassPaths = Lists.newArrayList();
// 同包下的路径下的文件,同包下的 不会import , 按方法名 查找调用, 首次循环不执行
if (!StringUtils.equals(absolutePath, searchFolder)) {
samePackageClassPaths = findSamePackageClassPath(absolutePath, methodNames, interfaceClassName);
allPath.addAll(samePackageClassPaths);
}
// 调用远程方法 所在的类
List<String> paths = findFileBySearchText(searchFolder, interfaceName + "." + interfaceClassName);
allPath.addAll(paths);
System.out.println("searchText : " + interfaceName + "." + interfaceClassName + " " + methodNames +" \n samePackageClassPaths:" + samePackageClassPaths
+ "\n paths: " + paths);
for (String path : allPath) {
// 读取Java源文件内容
String sourceCode = new String(Files.readAllBytes(Paths.get(path)));
// 创建JavaParser实例并解析源代码
CompilationUnit cu = StaticJavaParser.parse(sourceCode);
if (!cu.getPackageDeclaration().isPresent()) {
System.out.println("=====" + cu.toString());
continue;
}
Context context = new Context();
context.setInterfacePackageName(interfaceName);
context.setInterfaceClassName(interfaceClassName);
// 所在包名
String packageName = cu.getPackageDeclaration().get().getName().toString();
context.setPackageName(packageName);
//是否注入了 对应的service
boolean variableName = findVariableName(cu, context);
if (!variableName) {
continue;
}
// 变量所在方法名
Set<MethodDeclaration> methodSet = Sets.newHashSet();
List<MethodDeclaration> methodDeclarations = analyzeMethodCode(cu, methodNames, methodSet);
if (CollectionUtils.isEmpty(methodDeclarations)) {
continue;
}
methodDeclarations = methodDeclarations.stream().filter(m -> methodIsPublic(m)).collect(Collectors.toList());
List<String> methods = methodDeclarations.stream()
.map(method -> method.getNameAsString())
.collect(Collectors.toList());
if (path.toLowerCase().endsWith("controller.java")) {
getControllerInterfaceUrl(searchText, path, methodDeclarations);
continue;
}
context.setMethodName(methods);
extracted(searchText, path, context.getMethodName(), context.getPackageName(), context.getClassName());
}
}
private static void getControllerInterfaceUrl(String searchText, String path, List<MethodDeclaration> methodDeclarations) {
for (MethodDeclaration methodDeclaration : methodDeclarations) {
if (!isControllerMethod(methodDeclaration)) {
continue;
}
List<StringLiteralExpr> list = methodDeclaration.findAll(StringLiteralExpr.class);
if (CollectionUtils.isNotEmpty(list)) {
StringLiteralExpr interfaceUrl =
list.stream()
// 此处修改判断为controll提供的接口
.filter(e -> e.getValue().endsWith(".htm"))
.findFirst().get();
String interfaceHtmlName = interfaceUrl.getValue();
System.out.println("find controller:" + path + " interface: " + interfaceHtmlName);
List<String> htmls = interfacesDubboMap.getOrDefault(searchText, Lists.newArrayList());
htmls.add(interfaceHtmlName);
}
}
}
private static boolean methodIsPublic(MethodDeclaration methodDeclaration) {
return methodDeclaration.getModifiers().stream().anyMatch(x -> x.getKeyword().equals(Modifier.Keyword.PUBLIC));
}
private static List<String> findFileBySearchText(String searchFolder, String searchText) {
List<String> findPath = Lists.newArrayList();
try (Stream<Path> paths = Files.walk(Paths.get(searchFolder))) {
// 过滤掉隐藏文件
paths.filter(Files::isRegularFile)
// 排除测试类
.filter(x -> x.toString().endsWith(".java") && !x.toString().endsWith("TestM.java"))
.forEach(filePath -> {
try (BufferedReader reader = Files.newBufferedReader(filePath)) {
String line;
while ((line = reader.readLine()) != null) {
if (line.contains(searchText)) {
System.out.println("searchFile:" + searchText + " Found match in file: " + filePath.toAbsolutePath());
findPath.add(filePath.toAbsolutePath().toString());
break; // 如果只需要找到第一处匹配就可退出,则使用break;否则注释掉此行以查找所有匹配行
}
}
} catch (IOException e) {
System.err.format("Encountered an error reading file %s%n", filePath);
e.printStackTrace();
}
});
} catch (IOException e) {
e.printStackTrace();
}
return findPath;
}
private static List<MethodDeclaration> analyzeMethodCode(CompilationUnit cu, List<String> methodNames, Set<MethodDeclaration> methodSet) throws Exception {
// 遍历并打印出所有的方法声明
int size = methodSet.size();
for (String methodName : methodNames) {
StringInMethodVisitor visitor = new StringInMethodVisitor(methodName);
visitor.visit(cu, null);
// 输出包含目标字符串的方法名及其所在位置
List<MethodDeclaration> matchingMethods = visitor.getMatchingMethods();
for (MethodDeclaration method : matchingMethods) {
methodSet.add(method);
}
}
// 存在表示是 controller中对外的方法,一般不回在controller内重复调用同一个RequestMapping方法
List<MethodDeclaration> declarationList = methodSet.stream().filter(x -> !isControllerMethod(x)).collect(Collectors.toList());
int size2 = declarationList.size();
if (size != size2) {
List<String> methods = declarationList.stream().map(x -> x.getNameAsString()).collect(Collectors.toList());
List<MethodDeclaration> newSet = analyzeMethodCode(cu, methods, Sets.newHashSet(declarationList));
methodSet.addAll(newSet);
}
return Lists.newArrayList(methodSet);
}
private static boolean isControllerMethod(MethodDeclaration methodDeclaration) {
NodeList<AnnotationExpr> annotations = methodDeclaration.getAnnotations();
Optional<AnnotationExpr> requestMapping = annotations.stream().filter(x -> x.getNameAsString().contains("RequestMapping")).findAny();
return requestMapping.isPresent();
}
public static boolean findVariableName(CompilationUnit cu, Context context) {
boolean findQuoteVariable = false;
for (TypeDeclaration type : cu.getTypes()) {
if (type instanceof ClassOrInterfaceDeclaration) {
ClassOrInterfaceDeclaration decl = (ClassOrInterfaceDeclaration) type;
context.setClassName(decl.getNameAsString());
((ClassOrInterfaceDeclaration) type).getImplementedTypes().forEach(item -> {
if (item.getNameAsString().toLowerCase().contains("service")) {
System.out.println("Found ClassOrInterfaceDeclaration : " + type.getNameAsString());
context.setClassName(item.getNameAsString());
}
});
}
for (Object field : type.getFields()) {
if (!(field instanceof FieldDeclaration)) {
continue;
}
FieldDeclaration fieldDecl = ((FieldDeclaration) field);
List<VariableDeclarator> variables = fieldDecl.getVariables();
Optional<VariableDeclarator> declaratorOptional = variables.stream()
.filter(var -> var.getTypeAsString().equals(context.getInterfaceClassName()))
.findFirst();
if (declaratorOptional.isPresent()) {
context.setDeclarationName(declaratorOptional.get().getName().toString());
findQuoteVariable = true;
}
}
}
return findQuoteVariable;
}
// 创建一个自定义访问者来查找包含目标字符串的方法
static class StringInMethodVisitor extends VoidVisitorAdapter<Void> {
private List<MethodDeclaration> matchingMethods = new ArrayList<>();
private String targetString;
public StringInMethodVisitor(String targetString) {
this.targetString = targetString;
}
@Override
public void visit(MethodDeclaration md, Void arg) {
if (md.toString().toLowerCase().contains(targetString.toLowerCase())) {
matchingMethods.add(md);
}
super.visit(md, arg);
}
public List<MethodDeclaration> getMatchingMethods() {
return matchingMethods;
}
}
}
@Data
public class Context {
String interfaceClassName;
String interfacePackageName;
String packageName;
String className;
List<String> methodName;
String declarationName;
}