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

Java 反射動態載入類別並呼叫 main() 方法實作

     編輯:Java 相關知識

應用場景

  • 外掛/模組系統:根據設定檔動態載入實作類別
  • 測試框架:動態發現並執行測試類別
  • 命令列工具:根據參數決定執行哪個主類別
  • 熱部署/重載:無需重啟 JVM 更新類別邏輯

核心 API

方法用途
Class.forName(String)載入類別,回傳 Class 物件
Class.getMethod(String, Class...)取得公開方法(含繼承)
Method.invoke(Object, Object...)呼叫方法,第一參數為實例(static 方法傳 null)

完整實作:DynamicLoader

import java.lang.reflect.Method;

public class DynamicLoader {
    public static void main(String[] args) throws Exception {
        if (args.length == 0) {
            System.out.println("用法: java DynamicLoader <完整類別名> [參數...]");
            return;
        }

        // 1. 動態載入類別
        String className = args[0];
        Class clazz = Class.forName(className);

        // 2. 準備 main() 方法參數(去掉類別名)
        String[] mainArgs = new String[args.length - 1];
        System.arraycopy(args, 1, mainArgs, 0, mainArgs.length);

        // 3. 尋找 public static void main(String[])
        Method mainMethod = findMainMethod(clazz);
        if (mainMethod == null) {
            System.err.println("找不到 public static void main(String[]) 方法: " + className);
            return;
        }

        // 4. 呼叫 main 方法(static 方法傳 null)
        mainMethod.invoke(null, (Object) mainArgs);
    }

    private static Method findMainMethod(Class clazz) {
        for (Method m : clazz.getMethods()) {
            if ("main".equals(m.getName()) 
                && java.lang.reflect.Modifier.isStatic(m.getModifiers())
                && m.getParameterCount() == 1
                && m.getParameterTypes()[0].equals(String[].class)) {
                return m;
            }
        }
        return null;
    }
}

測試類別:EchoIt

public class EchoIt {
    public static void main(String[] args) {
        for (int i = 0; i < args.length; i++) {
            System.out.println("Echo arg[" + i + "] = " + args[i]);
        }
    }
}

編譯與執行

javac DynamicLoader.java EchoIt.java
java DynamicLoader EchoIt ONE TWO THREE

輸出結果

Echo arg[0] = ONE
Echo arg[1] = TWO
Echo arg[2] = THREE

進階主題

載入自訂路徑的類別(URLClassLoader)

import java.net.URL;
import java.net.URLClassLoader;

public class CustomPathLoader {
    public static void main(String[] args) throws Exception {
        // 指定額外 classpath
        URL[] urls = { new File("/path/to/plugins/").toURI().toURL() };
        URLClassLoader loader = new URLClassLoader(urls, CustomPathLoader.class.getClassLoader());
        
        Class clazz = loader.loadClass("com.example.PluginImpl");
        // ... 後續同 DynamicLoader
    }
}

實例化並呼叫非 static 方法

Class clazz = Class.forName("com.example.ServiceImpl");
Object instance = clazz.getDeclaredConstructor().newInstance();  // 預設建構子

Method process = clazz.getMethod("process", String.class);
String result = (String) process.invoke(instance, "input data");

介面導向設計(推薦)

// 定義介面
public interface Plugin {
    void execute(String[] args);
}

// 動態載入實作
Class clazz = Class.forName("com.example.MyPlugin");
Plugin plugin = (Plugin) clazz.getDeclaredConstructor().newInstance();
plugin.execute(args);  // 型別安全,不需反射呼叫方法

常見錯誤與解決

錯誤原因解決
ClassNotFoundException類別名稱錯誤、classpath 不含該類別檢查完整類別名、確認 jar/目錄在 classpath
NoSuchMethodException找不到符合簽名的方法確認方法為 public static void main(String[])
IllegalAccessException方法非 public確認方法修飾詞、或用 setAccessible(true)
InvocationTargetException目標方法拋出例外檢查 e.getCause() 取得真實例外
ClassCastException類別未實作預期介面確認實作關係、或用介面導向設計

效能考量

  • 反射比直接呼叫慢(JIT 難優化、存取檢查開銷)
  • 熱點路徑避免頻繁反射,可快取 Method 物件
  • MethodHandles (Java 7+) 比 Method.invoke 更快

安全性提醒

動態載入類別若接受外部輸入(如 HTTP 參數指定類別名),極高風險:攻擊者可載入任意類別執行惡意代碼。必須實施白名單驗證、安全管理器、或限制於受信任來源。

總結

Class.forName() + 反射 API 實現動態類別載入與方法呼叫。核心流程:載入類別 → 尋找方法 → 準備參數 → invoke。現代框架(Spring、Dubbo、Pf4j)皆建立在此基礎之上,提供更完善的生命週期管理、依賴注入、隔離機制。實務上優先使用成熟框架,僅在簡單工具或特殊需求時手寫反射邏輯。

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