
JavaParser解析java文件
使用 JavaParser 解析 Java 文件内容
·
Java 文件解析
需求:上传一个java文件解析出特定的方法及参数信息
使用 JavaParser 解析 Java 文件内容:
JavaParser 是一个强大的 Java 代码解析器。你可以使用它来提取类、方法、注释和注解信息。首先添加 JavaParser 依赖到 pom.xml
:
<dependency>
<groupId>com.github.javaparser</groupId>
<artifactId>javaparser-core</artifactId>
<version>3.23.1</version>
</dependency>
代码实现逻辑:
package com.example.utils;
import com.github.javaparser.JavaParser;
import com.github.javaparser.ParserConfiguration;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.PackageDeclaration;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.body.Parameter;
import com.github.javaparser.ast.comments.Comment;
import com.github.javaparser.ast.type.Type;
import com.github.javaparser.ast.visitor.VoidVisitorAdapter;
import com.github.javaparser.resolution.types.ResolvedType;
import com.github.javaparser.symbolsolver.JavaSymbolSolver;
import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSolver;
import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver;
import lombok.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.util.Strings;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.util.*;
/**
* @version 1.0
* 解析java 文件
* @date 2024/5/28 15:23
*/
@Slf4j
public class ParseJavaFileUtil {
public static final String REGEX = "/\\s+|\\*|\\t|\r\n|\r/";
public static final String MAIN_TAG = "@Main";
public static final String DESC_TAG = "@desc";
public static final String PARAM_TAG = "@param";
public static final String RETURN_TAG = "@return";
public static final String PROPERTY_TAG = "@property";
public static final String DOLLAR = "$";
public static final String NOUN = ":";
public static final String DOT = ".";
private ParseJavaFileUtil() {
// do nothing, aim to private the constructor
}
/**
* @param bytesContent Java 文件内容
* @description: 解析 Java 文件
*/
public static void parseJavaFile(byte[] bytesContent, JavaSourceFileInfo info) {
String content = new String(bytesContent, StandardCharsets.UTF_8);
// 配置TypeSolver
CombinedTypeSolver typeSolver = new CombinedTypeSolver();
typeSolver.add(new ReflectionTypeSolver());
// 设置JavaParser的符号解析器
JavaSymbolSolver symbolSolver = new JavaSymbolSolver(typeSolver);
// 解析Java文件
JavaParser parser = new JavaParser();
ParserConfiguration parserConfiguration = parser.getParserConfiguration();
parserConfiguration.setSymbolResolver(symbolSolver);
CompilationUnit compilationUnit = parser.parse(content).getResult().orElse(null);
if (compilationUnit == null) {
log.error("解析代码包失败,请检查文件后重试");
}
// 获取包声明
Optional<PackageDeclaration> packageDeclaration = compilationUnit.getPackageDeclaration();
String packageName = packageDeclaration.map(PackageDeclaration::getNameAsString).orElse(StringUtils.EMPTY);
if (Strings.isEmpty(packageName)) {
log.error("解析代码包失败,请检查文件【包声明】后重试");
}
// 获取顶层类
List<ClassOrInterfaceDeclaration> topLevelClasses = new ArrayList<>();
Map<String, List<ParameterInfo>> classPropertyInfoMap = new HashMap<>();
// 获取类属性信息
getclassPropertyInfo(compilationUnit, topLevelClasses, packageName, classPropertyInfoMap);
// 各类属性
info.setClassPropertyInfoMap(classPropertyInfoMap);
if (topLevelClasses.isEmpty()) {
log.error("代码包内至少应有一个顶级主类");
}
if (topLevelClasses.size() > 1) {
log.error("代码包内有且仅允许有一个顶级主类");
}
// 解析类
ClassOrInterfaceDeclaration declaration = topLevelClasses.get(0);
// 包名
info.setPackageName(packageName);
// 主类名
info.setClassName(declaration.getNameAsString());
if (Strings.isEmpty(info.getClassName())) {
log.error("解析代码包失败,请检查文件【类名】后重试");
}
// 获取类的注释
Comment comment = declaration.getComment().orElse(null);
if (comment != null) {
// 删除所有的空格、回车、换行符、*、tab
String string = comment.getContent().replaceAll(REGEX, StringUtils.EMPTY).trim();
info.setClassDesc(string);
}
// 获取类名并加上包路径
String fullClassName = packageName.isEmpty() ? declaration.getNameAsString() : declaration.getFullyQualifiedName().orElse(StringUtils.EMPTY);
List<MethodDeclaration> methodList = declaration.getMethods().stream().filter(method -> {
Comment methodComment = method.getComment().orElse(null);
if (methodComment == null) {
return false;
}
List<String> list = methodComment.stream().map(Object::toString).toList();
return list.stream().anyMatch(e -> e.contains(MAIN_TAG));
}).toList();
if (methodList.isEmpty()){
log.error("代码包内至少应有一个主方法 并由 @Main 修饰");
}
if (methodList.size() > 1) {
log.error("代码包最多允许一个主方法由 @Main 修饰");
}
// 遍历类的方法
MethodDeclaration method = methodList.get(0);
// 获取方法的注释
Comment methodComment = method.getComment().orElse(null);
// 主方法名
info.setMainMethodName(method.getNameAsString());
if (methodComment != null) {
info.setMainMethodDesc(methodComment.getContent().replaceAll(REGEX, StringUtils.EMPTY).trim());
}
// 入参个数
int parameterCont = method.getParameters().size();
// 方法返回类型
ResolvedType resolve = method.getType().resolve();
ParameterInfo returnTypeInfo = ParameterInfo.builder().build();
// 获取返回类型信息
getReturnTypeParameterInfo(resolve, returnTypeInfo, fullClassName, method, classPropertyInfoMap);
// 返回类型信息
info.setReturnTypeInfo(returnTypeInfo);
// 参数个数
info.setParameterCount(parameterCont);
List<ParameterInfo> parameterInfoList = new ArrayList<>();
// 获取参数信息
getParameterInfo(info, parameterCont, method, fullClassName, classPropertyInfoMap, parameterInfoList);
// 参数信息
info.setParameterInfoList(parameterInfoList);
}
/**
* @param resolve 类型
* @param returnTypeInfo 返回类型信息
* @param fullClassName 全类名
* @param method 方法
* @param classPropertyInfoMap 类属性信息
*/
private static void getReturnTypeParameterInfo(ResolvedType resolve, ParameterInfo returnTypeInfo, String fullClassName, MethodDeclaration method, Map<String, List<ParameterInfo>> classPropertyInfoMap) {
String describe = resolve.describe();
if (resolve.isVoid()) {
returnTypeInfo.setParameterType(StringUtils.EMPTY);
return;
}
if (describe.contains(fullClassName) && !describe.equals(fullClassName)) {
// 如果是内部类,则获取完整类名
String s = fullClassName + DOT;
while (describe.contains(s)) {
describe = describe.replace(s, fullClassName + DOLLAR);
}
}
// 如果是对象类型 则获取完整类名 赋值 innerParameterInfo
String parameterRefType = getParameterRefType(method.getType());
String parameterType = getParameterType(resolve);
returnTypeInfo.setParameterRefType(parameterRefType);
returnTypeInfo.setInnerParameterInfo(classPropertyInfoMap.getOrDefault(returnTypeInfo.getParameterRefType(), null));
returnTypeInfo.setFullParameterType(describe);
returnTypeInfo.setParameterType(parameterType);
// 返回类型描述
Comment returnTypeComment = method.getComment().orElse(null);
if (returnTypeComment == null) {
log.error("解析代码包失败,请检查文件【方法返回类型描述】后重试");
}
// 解析字段注释->1、去掉空行 将注释按照换行符分割为数组 2、@return 开头的是参数,参数格式为: 英文名:中文名:是否必填
String[] split = getCommentArray(returnTypeComment.getContent());
// 通过注释获取参数信息
for (String s : split) {
if (Strings.isEmpty(s)) {
continue;
}
// 获取参数信息: @return:英文名:中文名:是否必填
getDescTag(s, returnTypeInfo);
// 获取参数信息: @return:英文名:中文名:是否必填
getNameCnNameAndRequired(s, returnTypeInfo, RETURN_TAG);
}
}
/**
* 获取完整类名
* @param type 类型
* @return 返回类型
*/
private static String getParameterRefType(Type type) {
List<Node> childNodes = type.getChildNodes();
String parameterRefType = StringUtils.EMPTY;
// 如果是数组类型
if (type.isArrayType()) {
log.error("暂不支持形如 int[]、Object[] 等数组类型");
}
// 如果有子节点
if (Objects.nonNull(childNodes) && !childNodes.isEmpty()){
// 复杂类型 一个子节点就取第0个 多个子节点取第1个
parameterRefType = childNodes.get(childNodes.size() == 1 ? 0 : 1).toString();
}
return parameterRefType;
}
/**
* 获取参数信息
* @param info 类信息
* @param parameterCont 参数个数
* @param method 方法
* @param fullClassName 全类名
* @param classPropertyInfoMap 类属性信息
* @param parameterInfoList 参数信息列表
*/
private static void getParameterInfo(JavaSourceFileInfo info, int parameterCont, MethodDeclaration method, String fullClassName, Map<String, List<ParameterInfo>> classPropertyInfoMap, List<ParameterInfo> parameterInfoList) {
for (int i = 0; i < parameterCont; i++) {
Parameter parameter = method.getParameters().get(i);
// 解析参数类型并获取完整类名
ResolvedType resolvedType = parameter.getType().resolve();
String qualifiedName = resolvedType.describe();
// 判断参数类型是否为内部类 直接用 qualifiedName 是否包含 fullClassName 判断即可
if (qualifiedName.contains(fullClassName) && !qualifiedName.equals(fullClassName)) {
// 如果是内部类,则获取完整类名
String target = info.getClassName() + DOT;
while (qualifiedName.contains(target)) {
qualifiedName = qualifiedName.replace(target, info.getClassName() + DOLLAR);
}
}
// 如果是对象类型 则获取完整类名 赋值 innerParameterInfo
String parameterRefType = getParameterRefType(parameter.getType());
String parameterType = getParameterType(resolvedType);
ParameterInfo build = ParameterInfo.builder()
.parameterPosition(i)
.fullParameterType(qualifiedName)
.parameterType(parameterType)
.parameterRefType(parameterRefType)
.innerParameterInfo(classPropertyInfoMap.getOrDefault(parameterRefType, null))
.build();
String parameterComment = method.getComment().toString();
// 解析字段注释->1、去掉空行 将注释按照换行符分割为数组 2、@desc 开头的是描述 3、@param 开头的是参数,参数格式为: 英文名:中文名:是否必填
String[] split = getCommentArray(parameterComment);
// 通过注释获取参数信息
for (String s : split) {
if (Strings.isEmpty(s)) {
continue;
}
// 获取描述: @desc:描述
getDescTag(s, build);
// 获取参数信息: @param:参数位置:英文名:中文名:是否必填
getPositionNameCnNameAndRequired(s, build);
}
parameterInfoList.add(build);
}
}
/**
* 获取类属性、类信息
*
* @param compilationUnit 编译单元
* @param topLevelClasses 顶层类
* @param packageName 包名
* @param classPropertyInfoMap 类属性信息
*/
private static void getclassPropertyInfo(CompilationUnit compilationUnit, List<ClassOrInterfaceDeclaration> topLevelClasses, String packageName, Map<String, List<ParameterInfo>> classPropertyInfoMap) {
compilationUnit.accept(new VoidVisitorAdapter<Void>() {
@Override
public void visit(ClassOrInterfaceDeclaration n, Void arg) {
super.visit(n, arg);
// 获取顶层类
Optional<Node> parentNode = n.getParentNode();
if (parentNode.isEmpty() || !(parentNode.get() instanceof ClassOrInterfaceDeclaration)) {
topLevelClasses.add(n);
}
// 获取类属性
List<ParameterInfo> parameterInfoList = getParameterInfoList(n, packageName);
// 类属性
classPropertyInfoMap.put(n.getNameAsString(), parameterInfoList);
}
}, null);
}
/**
* 获取类属性
*
* @param n 类
* @param packageName 包名
* @return 属性信息
*/
private static List<ParameterInfo> getParameterInfoList(ClassOrInterfaceDeclaration n, String packageName) {
List<ParameterInfo> parameterInfoList = new ArrayList<>();
n.getFields().forEach(field -> {
ParameterInfo parameterInfo = new ParameterInfo();
// 获取字段类型
ResolvedType resolvedType = field.resolve().getType();
String describe = resolvedType.describe();
if (describe.contains(packageName)) {
// 如果是内部类,则获取完整类名
String target = DOT + n.getNameAsString();
while (describe.contains(target)) {
describe = describe.replace(target, DOLLAR + n.getNameAsString());
}
}
parameterInfo.setFullParameterType(describe);
String parameterType = getParameterType(resolvedType);
parameterInfo.setParameterType(parameterType);
// 获取字段注释
String comment = field.getComment().map(Comment::getContent).orElse(StringUtils.EMPTY);
// 解析字段注释->1、去掉空行 将注释按照换行符分割为数组 2、@desc 开头的是描述 3、@param 开头的是参数,参数格式为: 英文名:中文名:是否必填
if (StringUtils.isEmpty(comment)) {
return;
}
String[] split = getCommentArray(comment);
// 通过注释获取参数信息
for (String s : split) {
if (Strings.isEmpty(s)) {
continue;
}
// 获取描述: @desc:描述
getDescTag(s, parameterInfo);
// 获取参数信息: @property:英文名:中文名:是否必填
getNameCnNameAndRequired(s, parameterInfo, PROPERTY_TAG);
}
parameterInfoList.add(parameterInfo);
});
return parameterInfoList;
}
/**
* 获取参数类型
*
* @param resolvedType 参数类型
* @return 参数类型
*/
private static String getParameterType(ResolvedType resolvedType) {
String parameterType = resolvedType.describe();
// 如果是数组类型
if (resolvedType.isArray()) {
log.error("暂不支持形如 int[]、Object[] 等数组类型");
}
// 如果不是基本类型 则获取完整类名
if (resolvedType.isReferenceType()){
parameterType = resolvedType.asReferenceType().getQualifiedName();
}
return parameterType;
}
/**
* 获取注释数组 每一行一个元素
*
* @param comment 注释
* @return 注释数组
*/
private static String[] getCommentArray(String comment) {
return comment.replaceAll("/\\*\\*|\\*/", "").replaceAll(" *\\* ?", "").split("\r\n|\n");
}
/**
* 获取参数信息 @param s: 英文名:中文名:是否必填
*
* @param s 参数注释
* @param parameterInfo 参数信息
* @param tagName 标签名
*/
private static void getNameCnNameAndRequired(String s, ParameterInfo parameterInfo, String tagName) {
if (s.replaceAll("\\s+", "").startsWith(tagName)) {
String[] split1 = s.substring(s.indexOf(tagName) + tagName.length()).trim().split(NOUN);
if (split1.length == 3) {
parameterInfo.setParameterName(split1[0]);
parameterInfo.setParameterCnName(split1[1]);
parameterInfo.setRequired(Boolean.TRUE.toString().equals(split1[2]));
}
}
}
/**
* 获取参数信息 @param s: 参数位置:英文名:中文名:是否必填
*
* @param s 参数注释
* @param parameterInfo 参数信息
*/
private static void getPositionNameCnNameAndRequired(String s, ParameterInfo parameterInfo) {
if (s.replaceAll("\\s+", "").startsWith(PARAM_TAG)) {
String[] split1 = s.substring(s.indexOf(PARAM_TAG) + PARAM_TAG.length()).trim().split(NOUN);
// 给对应参数位置设置参数信息
if (split1.length == 4 && parameterInfo.getParameterPosition() == Integer.parseInt(split1[0])) {
parameterInfo.setParameterPosition(Integer.parseInt(split1[0]));
parameterInfo.setParameterName(split1[1]);
parameterInfo.setParameterCnName(split1[2]);
parameterInfo.setRequired(Boolean.TRUE.toString().equals(split1[3]));
}
}
}
/**
* 获取描述 @desc:描述
*
* @param s 参数注释
* @param parameterInfo 参数信息
*/
private static void getDescTag(String s, ParameterInfo parameterInfo) {
if (s.replaceAll("\\s+", "").startsWith(DESC_TAG)) {
parameterInfo.setParameterDesc(s.substring(s.indexOf(DESC_TAG) + DESC_TAG.length()).trim());
}
}
}
解析到的信息所用到的类(可根据实际需求修改)
import lombok.*;
import java.io.Serializable;
import java.util.List;
/**
* 出入参数信息
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@EqualsAndHashCode(callSuper = false)
public class ParameterInfo implements Serializable {
/**
* 参数位置
*/
private Integer parameterPosition;
/**
* 参数名称 中文
*/
private String parameterName;
/**
* 参数名称 英文
*/
private String parameterCnName;
/**
* 是否必需
*/
private Boolean required;
/**
* 参数类型 复合类型 包含其泛型 如 List<String> 则为 java.util.List<java.lang.String>
*/
private String fullParameterType;
/**
* 参数引用类型
*/
private String parameterRefType;
/**
* 参数类型 复合类型 不包含其泛型 如 List<String> 则为 java.util.List
*/
private String parameterType;
/**
* 参数描述
*/
private String parameterDesc;
/**
* 内部类参数信息
*/
private List<ParameterInfo> innerParameterInfo;
}
import lombok.*;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
/**
* java源文件信息
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@EqualsAndHashCode(callSuper = false)
public class JavaSourceFileInfo implements Serializable {
/**
* 文件名
*/
private String fileName;
/**
* 包路径
*/
private String packageName;
/**
* 类名
*/
private String className;
/**
* 类描述
*/
private String classDesc;
/**
* 主方法名
*/
private String mainMethodName;
/**
* 主方法描述
*/
private String mainMethodDesc;
/**
* 参数个数
*/
private Integer parameterCount;
/**
* 入参 参数信息
*/
private List<ParameterInfo> parameterInfoList;
/**
* 返回类型 复合类型返回类型信息
*/
private ParameterInfo returnTypeInfo;
/**
* 所有类属性信息 key:类名 value:属性信息
*/
private Map<String, List<ParameterInfo>> classPropertyInfoMap;
}
以上就可以解析出如下格式的java 文件的信息
package com.utils;
import java.util.List;
/**
* @desc TestUser类
*/
public class TestUser {
private TestUser() {
// private constructor
}
/**
* @Main
* @desc 获取UserName
* @param 0:userList:用户列表:true
* @return 英文:中文:是否一定返回
*/
public static List<User> getUserName(List<User> userList){
return userList;
}
/**
* @desc 用户内部类
*/
static class User{
/**
* @desc 姓名
* @property name:姓名:true
*/
private String name;
/**
* @desc 年龄
* @property age:年龄:true
*/
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
}
以上示例格式要求为
Java 文件格式正确,结构完整,有必要的包名、类名等。
Java 文件内只能有一个主要的方法,并需在注释里以 @Main
说明。
允许使用静态内部类,但不允许在一个文件类包含多个类。
代码注释需包含以下标识:
@Main
:标识主方法@desc
:标识描述信息@param
:标识参数信息,格式为@param 参数位置:参数英文名:参数中文名:是否必须
@property
:标识属性信息,格式为@property 参数英文名:参数中文名:是否必须
@return
:标识返回信息,格式为@return 参数英文名:参数中文名:是否必须
更多推荐
所有评论(0)