Java 文件解析
需求:上传一个java文件解析出特定的方法及参数信息
使用 JavaParser 解析 Java 文件内容:
JavaParser 是一个强大的 Java 代码解析器。你可以使用它来提取类、方法、注释和注解信息。首先添加 JavaParser 依赖到
:
<dependency>
<groupId></groupId>
<artifactId>javaparser-core</artifactId>
<version>3.23.1</version>
</dependency>
代码实现逻辑:
package ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import lombok.*;
import .slf4j.Slf4j;
import .;
import .;
import ;
import ;
import .*;
/**
* @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();
(new ReflectionTypeSolver());
// 设置JavaParser的符号解析器
JavaSymbolSolver symbolSolver = new JavaSymbolSolver(typeSolver);
// 解析Java文件
JavaParser parser = new JavaParser();
ParserConfiguration parserConfiguration = ();
(symbolSolver);
CompilationUnit compilationUnit = (content).getResult().orElse(null);
if (compilationUnit == null) {
("解析代码包失败,请检查文件后重试");
}
// 获取包声明
Optional<PackageDeclaration> packageDeclaration = ();
String packageName = (PackageDeclaration::getNameAsString).orElse();
if ((packageName)) {
("解析代码包失败,请检查文件【包声明】后重试");
}
// 获取顶层类
List<ClassOrInterfaceDeclaration> topLevelClasses = new ArrayList<>();
Map<String, List<ParameterInfo>> classPropertyInfoMap = new HashMap<>();
// 获取类属性信息
getclassPropertyInfo(compilationUnit, topLevelClasses, packageName, classPropertyInfoMap);
// 各类属性
(classPropertyInfoMap);
if (()) {
("代码包内至少应有一个*主类");
}
if (() > 1) {
("代码包内有且仅允许有一个*主类");
}
// 解析类
ClassOrInterfaceDeclaration declaration = (0);
// 包名
(packageName);
// 主类名
(());
if ((())) {
("解析代码包失败,请检查文件【类名】后重试");
}
// 获取类的注释
Comment comment = ().orElse(null);
if (comment != null) {
// 删除所有的空格、回车、换行符、*、tab
String string = ().replaceAll(REGEX, ).trim();
(string);
}
// 获取类名并加上包路径
String fullClassName = () ? () : ().orElse();
List<MethodDeclaration> methodList = ().stream().filter(method -> {
Comment methodComment = ().orElse(null);
if (methodComment == null) {
return false;
}
List<String> list = ().map(Object::toString).toList();
return ().anyMatch(e -> (MAIN_TAG));
}).toList();
if (()){
("代码包内至少应有一个主方法 并由 @Main 修饰");
}
if (() > 1) {
("代码包最多允许一个主方法由 @Main 修饰");
}
// 遍历类的方法
MethodDeclaration method = (0);
// 获取方法的注释
Comment methodComment = ().orElse(null);
// 主方法名
(());
if (methodComment != null) {
(().replaceAll(REGEX, ).trim());
}
// 入参个数
int parameterCont = ().size();
// 方法返回类型
ResolvedType resolve = ().resolve();
ParameterInfo returnTypeInfo = ().build();
// 获取返回类型信息
getReturnTypeParameterInfo(resolve, returnTypeInfo, fullClassName, method, classPropertyInfoMap);
// 返回类型信息
(returnTypeInfo);
// 参数个数
(parameterCont);
List<ParameterInfo> parameterInfoList = new ArrayList<>();
// 获取参数信息
getParameterInfo(info, parameterCont, method, fullClassName, classPropertyInfoMap, parameterInfoList);
// 参数信息
(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 = ();
if (()) {
();
return;
}
if ((fullClassName) && !(fullClassName)) {
// 如果是内部类,则获取完整类名
String s = fullClassName + DOT;
while ((s)) {
describe = (s, fullClassName + DOLLAR);
}
}
// 如果是对象类型 则获取完整类名 赋值 innerParameterInfo
String parameterRefType = getParameterRefType(());
String parameterType = getParameterType(resolve);
(parameterRefType);
(((), null));
(describe);
(parameterType);
// 返回类型描述
Comment returnTypeComment = ().orElse(null);
if (returnTypeComment == null) {
("解析代码包失败,请检查文件【方法返回类型描述】后重试");
}
// 解析字段注释->1、去掉空行 将注释按照换行符分割为数组 2、@return 开头的是参数,参数格式为: 英文名:中文名:是否必填
String[] split = getCommentArray(());
// 通过注释获取参数信息
for (String s : split) {
if ((s)) {
continue;
}
// 获取参数信息: @return:英文名:中文名:是否必填
getDescTag(s, returnTypeInfo);
// 获取参数信息: @return:英文名:中文名:是否必填
getNameCnNameAndRequired(s, returnTypeInfo, RETURN_TAG);
}
}
/**
* 获取完整类名
* @param type 类型
* @return 返回类型
*/
private static String getParameterRefType(Type type) {
List<Node> childNodes = ();
String parameterRefType = ;
// 如果是数组类型
if (()) {
("暂不支持形如 int[]、Object[] 等数组类型");
}
// 如果有子节点
if ((childNodes) && !()){
// 复杂类型 一个子节点就取第0个 多个子节点取第1个
parameterRefType = (() == 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 = ().get(i);
// 解析参数类型并获取完整类名
ResolvedType resolvedType = ().resolve();
String qualifiedName = ();
// 判断参数类型是否为内部类 直接用 qualifiedName 是否包含 fullClassName 判断即可
if ((fullClassName) && !(fullClassName)) {
// 如果是内部类,则获取完整类名
String target = () + DOT;
while ((target)) {
qualifiedName = (target, () + DOLLAR);
}
}
// 如果是对象类型 则获取完整类名 赋值 innerParameterInfo
String parameterRefType = getParameterRefType(());
String parameterType = getParameterType(resolvedType);
ParameterInfo build = ()
.parameterPosition(i)
.fullParameterType(qualifiedName)
.parameterType(parameterType)
.parameterRefType(parameterRefType)
.innerParameterInfo((parameterRefType, null))
.build();
String parameterComment = ().toString();
// 解析字段注释->1、去掉空行 将注释按照换行符分割为数组 2、@desc 开头的是描述 3、@param 开头的是参数,参数格式为: 英文名:中文名:是否必填
String[] split = getCommentArray(parameterComment);
// 通过注释获取参数信息
for (String s : split) {
if ((s)) {
continue;
}
// 获取描述: @desc:描述
getDescTag(s, build);
// 获取参数信息: @param:参数位置:英文名:中文名:是否必填
getPositionNameCnNameAndRequired(s, build);
}
(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) {
(new VoidVisitorAdapter<Void>() {
@Override
public void visit(ClassOrInterfaceDeclaration n, Void arg) {
(n, arg);
// 获取顶层类
Optional<Node> parentNode = ();
if (() || !(() instanceof ClassOrInterfaceDeclaration)) {
(n);
}
// 获取类属性
List<ParameterInfo> parameterInfoList = getParameterInfoList(n, packageName);
// 类属性
((), parameterInfoList);
}
}, null);
}
/**
* 获取类属性
*
* @param n 类
* @param packageName 包名
* @return 属性信息
*/
private static List<ParameterInfo> getParameterInfoList(ClassOrInterfaceDeclaration n, String packageName) {
List<ParameterInfo> parameterInfoList = new ArrayList<>();
().forEach(field -> {
ParameterInfo parameterInfo = new ParameterInfo();
// 获取字段类型
ResolvedType resolvedType = ().getType();
String describe = ();
if ((packageName)) {
// 如果是内部类,则获取完整类名
String target = DOT + ();
while ((target)) {
describe = (target, DOLLAR + ());
}
}
(describe);
String parameterType = getParameterType(resolvedType);
(parameterType);
// 获取字段注释
String comment = ().map(Comment::getContent).orElse();
// 解析字段注释->1、去掉空行 将注释按照换行符分割为数组 2、@desc 开头的是描述 3、@param 开头的是参数,参数格式为: 英文名:中文名:是否必填
if ((comment)) {
return;
}
String[] split = getCommentArray(comment);
// 通过注释获取参数信息
for (String s : split) {
if ((s)) {
continue;
}
// 获取描述: @desc:描述
getDescTag(s, parameterInfo);
// 获取参数信息: @property:英文名:中文名:是否必填
getNameCnNameAndRequired(s, parameterInfo, PROPERTY_TAG);
}
(parameterInfo);
});
return parameterInfoList;
}
/**
* 获取参数类型
*
* @param resolvedType 参数类型
* @return 参数类型
*/
private static String getParameterType(ResolvedType resolvedType) {
String parameterType = ();
// 如果是数组类型
if (()) {
("暂不支持形如 int[]、Object[] 等数组类型");
}
// 如果不是基本类型 则获取完整类名
if (()){
parameterType = ().getQualifiedName();
}
return parameterType;
}
/**
* 获取注释数组 每一行一个元素
*
* @param comment 注释
* @return 注释数组
*/
private static String[] getCommentArray(String comment) {
return ("/\\*\\*|\\*/", "").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+", "").startsWith(tagName)) {
String[] split1 = ((tagName) + ()).trim().split(NOUN);
if ( == 3) {
(split1[0]);
(split1[1]);
(().equals(split1[2]));
}
}
}
/**
* 获取参数信息 @param s: 参数位置:英文名:中文名:是否必填
*
* @param s 参数注释
* @param parameterInfo 参数信息
*/
private static void getPositionNameCnNameAndRequired(String s, ParameterInfo parameterInfo) {
if (("\\s+", "").startsWith(PARAM_TAG)) {
String[] split1 = ((PARAM_TAG) + PARAM_TAG.length()).trim().split(NOUN);
// 给对应参数位置设置参数信息
if ( == 4 && () == (split1[0])) {
((split1[0]));
(split1[1]);
(split1[2]);
(().equals(split1[3]));
}
}
}
/**
* 获取描述 @desc:描述
*
* @param s 参数注释
* @param parameterInfo 参数信息
*/
private static void getDescTag(String s, ParameterInfo parameterInfo) {
if (("\\s+", "").startsWith(DESC_TAG)) {
(((DESC_TAG) + DESC_TAG.length()).trim());
}
}
}
解析到的信息所用到的类(可根据实际需求修改)
import lombok.*;
import ;
import ;
/**
* 出入参数信息
*/
@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> 则为 <>
*/
private String fullParameterType;
/**
* 参数引用类型
*/
private String parameterRefType;
/**
* 参数类型 复合类型 不包含其泛型 如 List<String> 则为
*/
private String parameterType;
/**
* 参数描述
*/
private String parameterDesc;
/**
* 内部类参数信息
*/
private List<ParameterInfo> innerParameterInfo;
}
import lombok.*;
import ;
import ;
import ;
/**
* 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 ;
import ;
/**
* @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) {
= name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
= age;
}
}
}
以上示例格式要求为
Java 文件格式正确,结构完整,有必要的包名、类名等。
Java 文件内只能有一个主要的方法,并需在注释里以 @Main
说明。
允许使用静态内部类,但不允许在一个文件类包含多个类。
代码注释需包含以下标识:
-
@Main
:标识主方法 -
@desc
:标识描述信息 -
@param
:标识参数信息,格式为@param 参数位置:参数英文名:参数中文名:是否必须
-
@property
:标识属性信息,格式为@property 参数英文名:参数中文名:是否必须
-
@return
:标识返回信息,格式为@return 参数英文名:参数中文名:是否必须