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

Java 反射調用任意類靜態方法通用工具類實現

     編輯:Java 相關知識

場景

動態加載類、插件化架構、腳本引擎、測試框架等場景需在運行時根據字符串類名/方法名調用靜態方法。

核心代碼

1. 被調用目標類

package com.example.demo;

public class TargetService {
    public static Integer process(String input) {
        System.err.println("靜態方法執行: " + input);
        return input.length();
    }

    public static String concat(String a, String b, String c) {
        return a + b + c;
    }
}

2. 通用反射調用器

public final class StaticMethodInvoker {
    private StaticMethodInvoker() {}

    /**
     * 調用靜態方法
     * @param className 全限定類名
     * @param methodName 方法名
     * @param args 實參(按聲明順序)
     * @return 方法返回值(void 返回 null)
     */
    @SuppressWarnings("unchecked")
    public static Object invoke(String className, String methodName, Object... args) {
        try {
            // 1. 加載類
            Class<?> clazz = Class.forName(className, true, Thread.currentThread().getContextClassLoader());

            // 2. 解析參數類型
            Class<?>[] paramTypes = new Class[args.length];
            for (int i = 0; i < args.length; i++) {
                paramTypes[i] = args[i] != null ? args[i].getClass() : Object.class;
                // 注意:基本類型傳入包裝類時會匹配失敗,需額外處理
            }

            // 3. 獲取方法(支持重載,取首個匹配)
            Method method = findMethod(clazz, methodName, paramTypes);
            if (method == null) {
                throw new NoSuchMethodException("未找到匹配方法: " + methodName + Arrays.toString(paramTypes));
            }

            // 4. 設置可訪問(繞過 private/protected)
            method.setAccessible(true);

            // 5. 執行調用(靜態方法實例參數傳 null)
            return method.invoke(null, args);
        } catch (Exception e) {
            throw new RuntimeException("反射調用失敗: " + className + "#" + methodName, e);
        }
    }

    /** 簡易重載匹配:參數數量一致且實參類型可賦值給形參 */
    private static Method findMethod(Class<?> clazz, String name, Class<?>[] argTypes) {
        for (Method m : clazz.getDeclaredMethods()) {
            if (!m.getName().equals(name) || !Modifier.isStatic(m.getModifiers())) continue;
            Class<?>[] params = m.getParameterTypes();
            if (params.length != argTypes.length) continue;
            boolean match = true;
            for (int i = 0; i < params.length; i++) {
                if (argTypes[i] != Object.class && !params[i].isAssignableFrom(argTypes[i])) {
                    match = false; break;
                }
            }
            if (match) return m;
        }
        return null;
    }
}

3. 使用示例

public class Demo {
    public static void main(String[] args) {
        // 調用無參/單參
        Object r1 = StaticMethodInvoker.invoke("com.example.demo.TargetService", "process", "Hello Reflection");
        System.out.println("返回值: " + r1); // 17

        // 調用多參
        Object r2 = StaticMethodInvoker.invoke("com.example.demo.TargetService", "concat", "A", "B", "C");
        System.out.println("返回值: " + r2); // ABC
    }
}

進階增強點

問題解決方案
基本類型參數參數類型推斷時將包裝類映射為基本類型(Integer.class → int.class),或直接要求調用者傳入 int.class 等 Type Token
重載消歧引入 MethodType 描述符或要求調用者傳入參數類型數組 Class[]
性能優化緩存 Method 對象(ConcurrentHashMap<String, Method>),避免重複 Class.forName 和 getDeclaredMethods
異常透傳捕獲 InvocationTargetException 拋出 getCause() 保留原始堆棧
模塊化系統Java 9+ 需 --add-opens 或使用 MethodHandles.lookup() 替代 setAccessible

生產級簡化版(緩存 + 基本類型支持)

public final class FastStaticInvoker {
    private static final Map<String, Method> CACHE = new ConcurrentHashMap<>();

    public static Object invoke(String className, String methodName, Object... args) {
        String key = className + "#" + methodName + "#" + Arrays.toString(args);
        Method method = CACHE.computeIfAbsent(key, k -> resolveMethod(className, methodName, args));
        try {
            method.setAccessible(true);
            return method.invoke(null, args);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e.getCause());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private static Method resolveMethod(String className, String methodName, Object[] args) {
        try {
            Class<?> clazz = Class.forName(className);
            Class<?>[] types = Arrays.stream(args)
                    .map(o -> o != null ? primitiveIfWrapper(o.getClass()) : Object.class)
                    .toArray(Class[]::new);
            for (Method m : clazz.getDeclaredMethods()) {
                if (m.getName().equals(methodName) && Modifier.isStatic(m.getModifiers())
                        && Arrays.equals(m.getParameterTypes(), types)) {
                    return m;
                }
            }
            throw new NoSuchMethodException();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private static Class<?> primitiveIfWrapper(Class<?> c) {
        if (c == Integer.class) return int.class;
        if (c == Long.class) return long.class;
        if (c == Double.class) return double.class;
        if (c == Boolean.class) return boolean.class;
        if (c == Character.class) return char.class;
        if (c == Byte.class) return byte.class;
        if (c == Short.class) return short.class;
        if (c == Float.class) return float.class;
        return c;
    }
}
反射調用靜態方法屬於動態編程範疇,運行時靈活性極高但損失編譯期類型安全與性能,僅在插件系統、腳本引擎、通用測試框架等基礎設施層使用,業務代碼慎用。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved