You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用JavaParser 3.25.8获取Java类方法对自身字段的读写访问列表

解决JavaParser统计类字段访问的问题

问题需求

需要使用JavaParser 3.25.8实现以下功能:

  • 仅统计类的成员字段,排除方法内局部变量
  • 覆盖所有字段访问类型(赋值、自增、自减等)
  • 区分字段的读访问和写访问
  • 仅统计目标类自身的字段,排除其他类的字段

现有代码问题

当前代码仅通过FieldAccessExpr查找字段访问,存在两个核心问题:

  1. 漏统计自身类字段:类内直接访问自身字段(如field1 = 10中的field1)对应的AST节点是NameExpr,而非FieldAccessExpr,因此被遗漏。
  2. 错误统计外部类字段:System.out这类外部类的字段访问属于FieldAccessExpr,被错误统计进来。

同时,现有代码未实现读/写访问的区分,也没有校验字段是否属于当前类。

解决方案

利用JavaParser的Symbol Solver模块解析符号类型,结合AST节点分析实现需求:

  1. 初始化Symbol Solver,让JavaParser能识别字段的所属类
  2. 遍历方法内的所有表达式,同时处理NameExpr和FieldAccessExpr
  3. 通过符号解析判断访问的字段是否属于当前类
  4. 根据上下文区分读/写访问:
    • 赋值语句的左操作数、自增/自减表达式属于写访问
    • 赋值语句的右操作数、方法调用参数等属于读访问

修改后的完整代码

import com.github.javaparser.StaticJavaParser;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.FieldDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.expr.*;
import com.github.javaparser.resolution.declarations.ResolvedFieldDeclaration;
import com.github.javaparser.symbolsolver.JavaSymbolSolver;
import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSolver;
import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver;

import java.io.File;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;

public class FieldAccessList {

    public static void main(String[] args) throws IOException {
        // 初始化Symbol Solver,用于解析符号类型
        CombinedTypeSolver combinedTypeSolver = new CombinedTypeSolver();
        combinedTypeSolver.add(new ReflectionTypeSolver());
        JavaSymbolSolver symbolSolver = new JavaSymbolSolver(combinedTypeSolver);
        StaticJavaParser.getConfiguration().setSymbolResolver(symbolSolver);

        File sourceFile = new File("Example.java");
        CompilationUnit cu = StaticJavaParser.parse(sourceFile);

        cu.findAll(ClassOrInterfaceDeclaration.class).forEach(classDeclaration -> {
            System.out.println("Class: " + classDeclaration.getNameAsString());
            // 收集当前类的所有字段名称
            Set<String> classFields = new HashSet<>();
            classDeclaration.findAll(FieldDeclaration.class).forEach(field -> {
                field.getVariables().forEach(var -> classFields.add(var.getNameAsString()));
            });

            classDeclaration.findAll(MethodDeclaration.class).forEach(methodDeclaration -> {
                System.out.println("  Method: " + methodDeclaration.getNameAsString());

                // 处理赋值表达式的左值(写访问)
                methodDeclaration.findAll(AssignExpr.class).forEach(assignExpr -> {
                    processFieldAccess(assignExpr.getTarget(), classFields, "Write");
                });

                // 处理自增自减表达式(写访问)
                methodDeclaration.findAll(UnaryExpr.class).forEach(unaryExpr -> {
                    if (unaryExpr.getOperator() == UnaryExpr.Operator.POSTFIX_INCREMENT
                            || unaryExpr.getOperator() == UnaryExpr.Operator.POSTFIX_DECREMENT
                            || unaryExpr.getOperator() == UnaryExpr.Operator.PREFIX_INCREMENT
                            || unaryExpr.getOperator() == UnaryExpr.Operator.PREFIX_DECREMENT) {
                        processFieldAccess(unaryExpr.getExpression(), classFields, "Write");
                    }
                });

                // 处理读访问:所有不是写操作的字段访问
                methodDeclaration.findAll(NameExpr.class).forEach(nameExpr -> {
                    if (!isPartOfWriteOperation(nameExpr)) {
                        processFieldAccess(nameExpr, classFields, "Read");
                    }
                });

                methodDeclaration.findAll(FieldAccessExpr.class).forEach(fieldAccessExpr -> {
                    if (!isPartOfWriteOperation(fieldAccessExpr)) {
                        processFieldAccess(fieldAccessExpr, classFields, "Read");
                    }
                });
            });
        });
    }

    private static void processFieldAccess(Expr expr, Set<String> classFields, String accessType) {
        try {
            // 解析表达式对应的符号
            if (expr instanceof NameExpr) {
                ResolvedFieldDeclaration field = ((NameExpr) expr).resolve().asField();
                String fieldName = field.getName();
                if (classFields.contains(fieldName)) {
                    System.out.println("    " + accessType + " Access: " + fieldName);
                }
            } else if (expr instanceof FieldAccessExpr) {
                ResolvedFieldDeclaration field = ((FieldAccessExpr) expr).resolve().asField();
                String fieldName = field.getName();
                // 判断字段所属类是否为当前类(通过全限定名对比)
                String currentClassFullName = expr.findAncestor(ClassOrInterfaceDeclaration.class)
                        .map(c -> c.getFullyQualifiedName().orElse(""))
                        .orElse("");
                if (field.declaringType().getQualifiedName().equals(currentClassFullName)) {
                    System.out.println("    " + accessType + " Access: " + fieldName);
                }
            }
        } catch (Exception e) {
            // 非字段访问的情况,直接忽略
        }
    }

    private static boolean isPartOfWriteOperation(Expr expr) {
        // 判断表达式是否是赋值操作的左值,或者自增自减操作的目标
        Node parent = expr.getParentNode().orElse(null);
        if (parent instanceof AssignExpr) {
            return ((AssignExpr) parent).getTarget() == expr;
        }
        if (parent instanceof UnaryExpr) {
            UnaryExpr.Operator op = ((UnaryExpr) parent).getOperator();
            return op == UnaryExpr.Operator.POSTFIX_INCREMENT
                    || op == UnaryExpr.Operator.POSTFIX_DECREMENT
                    || op == UnaryExpr.Operator.PREFIX_INCREMENT
                    || op == UnaryExpr.Operator.PREFIX_DECREMENT;
        }
        return false;
    }
}

验证结果

运行修改后的代码,针对Example.java的输出如下:

Class: Example
  Method: method1
    Write Access: field1
    Read Access: field2
  Method: method2
    Write Access: field2

完全符合需求,既统计了自身类的所有字段访问,排除了外部类字段,还区分了读/写访问类型。

内容的提问来源于stack exchange,提问作者Gholamali Irani

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 10:04:56