框架開發、插件系統、腳本引擎整合時,常需在運行期根據類名、方法名動態調用目標方法。本文封裝一個類型安全、支援可變參數、異常統一轉換的泛型工具類 ReflectionInvoker,替代原始反射的冗長代碼。
int ↔ Integer)。ReflectionException。Method 對象,避免重複 getMethod 開銷。import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public final class ReflectionInvoker {
private static final Map<String, Method> METHOD_CACHE = new ConcurrentHashMap<>();
private ReflectionInvoker() {}
/**
* 動態調用目標對象的指定方法
*
* @param target 目標實例(非 static 方法)或 Class(static 方法)
* @param methodName 方法名
* @param args 實際參數值陣列
* @return 方法返回值(void 返回 null)
* @throws ReflectionException 找不到方法、參數不匹配、調用拋出異常
*/
@SuppressWarnings("unchecked")
public static <T> T invoke(Object target, String methodName, Object... args) {
Class<?> targetClass = (target instanceof Class) ? (Class<?>) target : target.getClass();
Class<?>[] paramTypes = toClassArray(args);
Method method = findMethod(targetClass, methodName, paramTypes);
if (method == null) {
throw new ReflectionException("方法不存在: " + targetClass.getName() + "#" + methodName + "(" + Arrays.toString(paramTypes) + ")");
}
try {
method.setAccessible(true);
Object result = method.invoke(target instanceof Class ? null : target, args);
return (T) result;
} catch (IllegalAccessException | InvocationTargetException e) {
Throwable cause = e instanceof InvocationTargetException ? e.getCause() : e;
throw new ReflectionException("調用失敗: " + method.toGenericString(), cause);
}
}
private static Method findMethod(Class<?> clazz, String name, Class<?>[] paramTypes) {
String key = clazz.getName() + "#" + name + "#" + Arrays.toString(paramTypes);
return METHOD_CACHE.computeIfAbsent(key, k -> {
// 精確匹配
try {
return clazz.getMethod(name, paramTypes);
} catch (NoSuchMethodException ignored) {}
// 兼容基本類型/包裝類型自動轉換
for (Method m : clazz.getMethods()) {
if (!m.getName().equals(name)) continue;
Class<?>[] mp = m.getParameterTypes();
if (mp.length != paramTypes.length) continue;
boolean match = true;
for (int i = 0; i < mp.length; i++) {
if (!isAssignable(mp[i], paramTypes[i])) { match = false; break; }
}
if (match) return m;
}
return null;
});
}
private static boolean isAssignable(Class<?> formal, Class<?> actual) {
if (formal.isPrimitive()) {
return wrapperToPrimitive(formal) == actual || formal == actual;
}
return formal.isAssignableFrom(actual);
}
private static Class<?> wrapperToPrimitive(Class<?> wrapper) {
if (wrapper == Integer.class) return int.class;
if (wrapper == Long.class) return long.class;
if (wrapper == Double.class) return double.class;
if (wrapper == Boolean.class) return boolean.class;
if (wrapper == Character.class) return char.class;
if (wrapper == Byte.class) return byte.class;
if (wrapper == Short.class) return short.class;
if (wrapper == Float.class) return float.class;
return wrapper;
}
private static Class<?>[] toClassArray(Object... args) {
if (args == null) return new Class[0];
Class<?>[] types = new Class[args.length];
for (int i = 0; i < args.length; i++) {
types[i] = args[i] != null ? args[i].getClass() : Object.class;
}
return types;
}
public static class ReflectionException extends RuntimeException {
public ReflectionException(String msg) { super(msg); }
public ReflectionException(String msg, Throwable cause) { super(msg, cause); }
}
}
// 1. 實例方法
UserService service = new UserServiceImpl();
User user = ReflectionInvoker.invoke(service, "findById", 1001L);
// 2. 靜態方法
String json = ReflectionInvoker.invoke(JsonUtil.class, "toJson", user);
// 3. 多態參數自動匹配
Number num = ReflectionInvoker.invoke(Calculator.class, "add", 1, 2L); // int + long -> Number
// 4. 異常處理
try {
ReflectionInvoker.invoke(bean, "riskyMethod");
} catch (ReflectionInvoker.ReflectionException e) {
log.error("反射調用異常", e);
}
ConcurrentHashMap 取得 Method。Long.valueOf(id) 而非 id 自動推導為 Integer),減少匹配輪詢。MethodHandle (Java 7+) 或 LambdaMetafactory (Java 8+) 生成直接呼叫點,效能接近直接調用。封裝健壯的反射調用器,能讓動態特性代碼保持簡潔、可測、可維護,同時兼顧效能與安全。