首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Eclipse JDT AST 架構詳解:節點繼承體系、Expression 子類全景與子類獲取實戰代碼

     編輯:Java 綜合教學

前言:Eclipse JDT 為什麼是 Java 靜態分析的基石?

Eclipse JDT (Java Development Tools) 提供了強大的 AST (Abstract Syntax Tree) API,是構建 Java 代碼分析、重構、生成工具的核心基礎。本文梳理 org.eclipse.jdt.core.dom 包中 AST 節點的繼承體系、Expression 關鍵分支的完整子類列表,並提供一段可運行的代碼實現「掃描 JAR 獲取指定父類的所有子類」。

一、AST 節點核心繼承樹

ASTNode (根節點)
 ├─ Expression (表達式節點)
 │    ├─ MethodInvocation (方法調用)
 │    ├─ ClassInstanceCreation (new 表達式)
 │    ├─ Assignment (賦值)
 │    ├─ InfixExpression (中綴表達式 a+b)
 │    ├─ ... (詳見下文完整列表)
 ├─ Statement (語句節點)
 │    ├─ Block (塊語句 {})
 │    ├─ IfStatement, ForStatement, WhileStatement...
 ├─ Declaration (聲明節點)
 │    ├─ TypeDeclaration (類/接口聲明)
 │    ├─ MethodDeclaration (方法聲明)
 │    ├─ FieldDeclaration (字段聲明)
 ├─ Type (類型節點)
 │    ├─ SimpleType, ParameterizedType, ArrayType...
 └─ ... (Name, Modifier, Annotation 等)

二、Expression 完整直系子類列表 (JDT 3.10+)

以下類均直接或間接繼承 Expression,涵蓋 Java 所有表達式形態:

  • TypeLiteral、MarkerAnnotation、BooleanLiteral、ConditionalExpression (三元運算)
  • StringLiteral、VariableDeclarationExpression、InfixExpression
  • SuperFieldAccess、NullLiteral、Assignment
  • SingleMemberAnnotation、ArrayInitializer、ArrayAccess
  • Name (通用名稱)、SuperMethodInvocation、Expression (抽象基類)
  • CharacterLiteral、ThisExpression、ClassInstanceCreation
  • PrefixExpression (前綴 ++i)、NormalAnnotation
  • CreationReference (構造方法引用 ::new)、TypeMethodReference
  • SimpleName (簡單標識符)、ArrayCreation
  • ParenthesizedExpression (括號表達式)、Annotation (註解基類)
  • MethodReference (方法引用 ::)、FieldAccess (字段訪問)
  • QualifiedName (限定名 a.b)、SuperMethodReference
  • InstanceofExpression、CastExpression (強轉)
  • ExpressionMethodReference、PostfixExpression (後綴 i++)
  • NumberLiteral、MethodInvocation (方法調用)、LambdaExpression (Lambda)

註:具體層級關係請參考 JDT 文檔或 IDE 中的類圖,以上為扁平化列表。

三、實戰:掃描 JDT JAR 獲取指定類的所有子類

1. 核心思路

  1. 使用 ClassLoader 加載目標 JAR (org.eclipse.jdt.core_x.x.x.jar)
  2. 遍歷 JAR 內所有 .class 文件
  3. 加載類並判斷 targetClass.isAssignableFrom(clazz) 且 clazz != targetClass

2. 完整可運行代碼

import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.Expression;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

public class JdtSubClassScanner {

    public static void main(String[] args) throws Exception {
        // 1. 定位 JDT JAR (請根據實際環境修改路徑)
        String jarPath = "C:\\0workspace\\AtiPlatf_cms\\libx\\org.eclipse.jdt.core_3.10.0.v20140902-0626.jar";
        
        // 2. 目標父類
        Class<?> parentClass = Expression.class; // 可換為 ASTNode.class 等
        
        // 3. 掃描並輸出
        List<Class<?>> subClasses = getSubClassesFromJar(jarPath, parentClass);
        System.out.println("共發現 " + subClasses.size() + " 個 " + parentClass.getSimpleName() + " 子類:");
        subClasses.forEach(c -> System.out.println("  " + c.getName()));
    }

    /**
     * 從 JAR 文件中掃描指定父類的所有非抽象子類
     */
    public static List<Class<?>> getSubClassesFromJar(String jarPath, Class<?> parentClass) 
            throws IOException, ClassNotFoundException {
        List<Class<?>> result = new ArrayList<>();
        
        // 建立只加載該 JAR 的隔離 ClassLoader,避免污染主 ClassLoader
        URL jarUrl = new URL("file:" + jarPath);
        try (URLClassLoader jarLoader = new URLClassLoader(new URL[]{jarUrl}, null)) {
            try (JarFile jarFile = new JarFile(jarPath)) {
                Enumeration<JarEntry> entries = jarFile.entries();
                while (entries.hasMoreElements()) {
                    JarEntry entry = entries.nextElement();
                    String name = entry.getName();
                    // 只處理 .class 文件,且在 org.eclipse.jdt.core.dom 包下
                    if (name.endsWith(".class") && name.startsWith("org/eclipse/jdt/core/dom/")) {
                        String className = name.substring(0, name.length() - 6).replace('/', '.');
                        try {
                            Class<?> clazz = jarLoader.loadClass(className);
                            // 過濾:是父類的子類、非父類本身、非接口、非抽象類(可選)
                            if (parentClass.isAssignableFrom(clazz) 
                                    && clazz != parentClass
                                    && !clazz.isInterface()
                                    && (clazz.getModifiers() & Modifier.ABSTRACT) == 0) {
                                result.add(clazz);
                            }
                        } catch (ClassNotFoundException | NoClassDefFoundError e) {
                            // 忽略依賴缺失的類
                        }
                    }
                }
            }
        }
        return result;
    }
}

3. 關鍵點說明

  • 隔離 ClassLoader:使用 URLClassLoader(parent=null) 僅加載目標 JAR,避免版本衝突污染應用主 ClassLoader。
  • 包路徑過濾:僅掃描 org/eclipse/jdt/core/dom/,大幅提升速度並減少雜訊。
  • 異常吞噬:NoClassDefFoundError 通常因依賴類不在該 JAR 中,直接忽略。
  • 過濾條件:可根據需要調整是否包含接口、抽象類、內部類等。

四、應用場景拓展

  • 代碼風格檢查器:遍歷 AST 檢測特定表達式模式(如禁止三元運算嵌套)。
  • 自動重構工具:識別 MethodInvocation 批量重命名/遷移 API。
  • 代碼生成器:基於 TypeDeclaration 生成 Builder/DTO/Mapper 代碼。
  • 依賴分析:解析 ImportDeclaration 繪製模塊依賴圖。

五、資源鏈接

結語

掌握 JDT AST 節點體系是進行 Java 靜態分析的入場磚。本文提供的繼承樹全景、Expression 子類清單與掃描工具代碼,可直接用於工具開發或作為查閱手冊。下一步建議深入學習 ASTVisitor 訪問者模式進行樹遍歷,以及 ASTRewrite 進行代碼修改重寫。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved