之前分享过一篇使用 AI 可视化 Java 项目的文章,同步在 AI 破局星球、知乎、掘金等地方都分享了。
原文在这里AI 编程:可视化 Java 项目
有很多人感兴趣,我打算写一个系列文章拆解这个项目,大家多多点赞支持~
今天分享的是第一篇:如何使用 Spoon + JavaParser 工具解析一个本地的 Java 项目。
解析这一步骤是整个项目的基础,是为了获得整个 Java 项目的元数据。
这个元数据包含什么呢?1)整个项目的所有的类信息;2)整个项目的所有方法信息
方法信息
序号 | 字段名称 | 字段描述 |
---|---|---|
1 | method_id | 方法唯一id标识 |
2 | project_name | |
3 | method_name | 方法名 |
4 | class_id | 方法所属类名 |
5 | param_type | 参数类型 |
6 | response_type | 返回类型 |
7 | begin_line | 方法内容开始行 |
8 | end_line | 方法内容结束行 |
9 | branch | 分支 |
10 | method_desc | 方法描述 |
11 | chat_desc | GPT 描述 |
12 | invoke_count | 被调用数量 |
13 | mermaid_flow_graph | 流程图数据 |
14 | flow_graph_ignored | 是否忽略流程图 |
15 | annotation_info | 注解信息 |
16 | annotation_type | 注解类型 |
17 | access_modifier | 修饰符 |
类信息
序号 | 字段名称 | 字段描述 |
---|---|---|
1 | class_id | 类唯一标识 |
2 | class_name | 类名 |
3 | project_name | 项目唯一标识 |
4 | package_name | 包名 |
5 | branch | 分支 |
6 | class_type | 类的类型 |
7 | chat_desc | GPT 类描述 |
8 | class_desc | 类注释 |
9 | annotation_info | 类注解 |
10 | method_annotation_info | 方法注解信息 |
11 | annotation_type | 注解类型 |
怎么拿到整个项目的类信息和方法信息呢?
首先我们需要一个类解析器、一个方法解析器。使用 Java 的反射,我们就能拿到具体类和方法的详细信息。
类解析器代码:
public void execute(List<CtType<?>> elements) {
classStructs = Lists.newArrayList();
for (CtType<?> type : elements) {
try {
// 匿名内部类和泛型会跳过解析
if (type.isAnonymous()) {
continue;
}
if (Objects.isNull(type.getPackage())) {
continue;
}
// 获取类的简单类名
String simpleClassName = type.getSimpleName();
// GPT 接口获取解释
String chatDesc = "";
// 获取类所属包
String packageName = type.getPackage().getQualifiedName();
// 获取类注释信息
String classComment = type.getDocComment();
// 判断接口还是类
ClassType classType = getClassType(type);
// 获取类注解信息
List<AnnotationInfo> annotationInfos = Lists.newArrayList();
List<CtAnnotation<?>> annotations = type.getAnnotations();
for (CtAnnotation<?> annotation : annotations) {
AnnotationInfo annotationInfo = new AnnotationInfo();
String annotationName = annotation.getAnnotationType().getSimpleName();
annotationInfo.setAnnotationName(annotationName);
Map<String, CtExpression> annotationValues = annotation.getValues();
for (Map.Entry<String, CtExpression> entry : annotationValues.entrySet()) {
String attributeName = entry.getKey();
Object attributeValue = entry.getValue();
annotationInfo.addAttributeName(attributeName, attributeValue.toString());
}
annotationInfos.add(annotationInfo);
}
// 构造类元信息
ClassStruct classStruct = buildClassStruct(simpleClassName, packageName, classType,
classComment, annotationInfos, chatDesc);
classStructs.add(classStruct);
} catch (Exception e) {
log.error("class parse error, className ==>{}, errMsg ==>", type.getSimpleName(), e);
}
}
// 类元信息入库
}
方法解析器
public void execute(List<CtType<?>> elements) {
methodStructs = Lists.newArrayList();
for (CtType<?> element : elements) {
if (element.isAnonymous()) {
continue;
}
if (Objects.isNull(element.getPackage())) {
continue;
}
// 获取包名
String packageName = element.getPackage().getQualifiedName();
// 获取类名
String className = element.getSimpleName();
// 获取方法列表
Set<CtMethod<?>> methods = element.getMethods();
for (CtMethod<?> method : methods) {
try {
// 获取简单方法名
String methodName = method.getSimpleName();
// 获取全限定参数
String signatureParameters = method.getSignature();
int firstIndex = method.getSignature().indexOf("(");
int lastIndex = method.getSignature().indexOf(")");
String parameters = signatureParameters.substring(firstIndex + 1, lastIndex);
List<String> methodParameters = Splitter.on(",").omitEmptyStrings().splitToList(parameters);
// 获取响应体类型
String responseType = method.getType().getQualifiedName();
// 获取方法开始的行
int startLine = method.getPosition().getLine();
// 获取方法结束的行
int endLine = method.getPosition().getEndLine();
// 获取方法注释
String methodComment = method.getDocComment();
// 获取方法包含的注解信息
List<AnnotationInfo> annotationInfos = Lists.newArrayList();
List<CtAnnotation<?>> annotations = method.getAnnotations();
for (CtAnnotation<?> annotation : annotations) {
AnnotationInfo annotationInfo = new AnnotationInfo();
String annotationName = annotation.getAnnotationType().getSimpleName();
annotationInfo.setAnnotationName(annotationName);
Map<String, CtExpression> annotationValues = annotation.getValues();
for (Map.Entry<String, CtExpression> entry : annotationValues.entrySet()) {
String attributeName = entry.getKey();
Object attributeValue = entry.getValue();
annotationInfo.addAttributeName(attributeName, attributeValue.toString());
}
annotationInfos.add(annotationInfo);
}
// 获取方法的访问修饰符
String accessModifier = "";
if (Objects.isNull(method.getVisibility())) {
accessModifier = "default";
} else {
accessModifier = method.getVisibility().toString();
}
String methodId = generateIdentityUtil.generateMethodId(MethodSignature.builder()
.packagePath(packageName)
.className(className)
.methodName(methodName)
.parameterTypeString(methodParameters)
.build(), endLine - startLine + 1);
MethodStruct old = null;
// 基于规则判断一波是否需要询问chat
int lineCount = endLine - startLine;
MethodStruct methodStruct = MethodStruct.builder()
.appCode(GlobalVariableUtil.getAppCodeName())
.methodId(methodId)
.methodName(methodName)
.classId(generateIdentityUtil.generateClassId(className, packageName, GlobalVariableUtil.getAppCodeName()))
.paramTypes(methodParameters)
.responseType(responseType)
.beginLine(startLine)
.endLine(endLine)
.branch(GlobalVariableUtil.getBranch())
.methodDesc(methodComment)
.annotationInfos(annotationInfos)
.accessModifier(accessModifier)
.invokeCount(old == null ? 0 : old.getInvokeCount())
.mermaidFlowGraph(old == null ? "" : old.getMermaidFlowGraph())
.build();
if (old == null) {
methodStructs.add(methodStruct);
}
} catch (Exception e) {
log.error("method parse error, className ==>{}, methodName ==>{}, errMsg ==>",
className, method.getSimpleName(), e);
}
}
}
// 方法元信息入库
}
通过这种方式,我们就能拿到整个 Java 项目的方法信息。
需要注意的是,我们这个时候还没有使用 AI 技术,所以这个元信息中部分字段是空的。
我们看到类解析器和方法解析器方法的入参都是 List<CtType<?>> elements
。
那么,这个信息从哪里来的呢?
我这里使用的是 Spoon 工具。
Spoon 是什么?
Spoon 框架常被用于解析和处理 Java 源代码。Spoon 是一个强大的源码分析与转换工具,它通过构建抽象语法树(Abstract Syntax Tree, AST)来表示 Java 源代码,并提供了一套丰富的 API 供开发者操作 AST。
Spoon 能够完整且准确地捕获源代码的所有细节,所以它非常适合于进行复杂的静态代码分析、重构、自动插入代码逻辑等工作。
Spoon 不会用?没关系,AI 可以帮你写代码。
我们可以看到,GPT 直接帮我们生成完整代码,我们只需要在对应的地方,替换成我们的类解析器和方法解析器即可。
提示词如下:
你是一个Java技术专家。
我需要解析本地的一个 Java 项目,获得这个项目中的类信息和方法信息。我会给你提供这个 Java 项目的绝对路径。
请你使用 Spoon 生成解析代码
写到这里,我要告诉你的是,其实类解析器和方法解析的代码,也可以交给 AI 来完成哟~ 你可以试试看,如果有问题,随时找阿七给你解答。
到这里,我们今天的内容就结束啦。
下一篇,我们分享使用 AI 生成方法的流程图,请期待~