Java 反射機制允許運行時動態加載類、創建實例、調用方法,是框架設計(Spring MVC、Struts、Dubbo)的核心基石。本文通過實現一個簡易的 Servlet 分發器,演示反射動態調用的完整流程:參數解析 → 類加載 → 方法查找 → 實例化 → 調用執行,並分析安全性與效能考量。
傳統模式:每個業務對應一個 Servlet,web.xml 配置膨脹。
反射模式:一個 DispatcherServlet 根據請求參數動態路由到目標處理器方法。
@WebServlet("/dispatcher")
public class DispatcherServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// 1. 獲取參數:目標類全限定名、方法名
String className = req.getParameter("className");
String methodName = req.getParameter("method");
if (className == null || methodName == null) {
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "缺少 className 或 method 參數");
return;
}
try {
// 2. 類加載(雙親委派模型)
Class> clazz = Class.forName(className);
// 3. 方法查找:匹配參數類型 (HttpServletRequest, HttpServletResponse)
Method method = clazz.getDeclaredMethod(methodName,
HttpServletRequest.class, HttpServletResponse.class);
// 4. 實例化(需無參構造器)
Object handler = clazz.getDeclaredConstructor().newInstance();
// 5. 方法調用
method.invoke(handler, req, resp);
} catch (ClassNotFoundException e) {
resp.sendError(HttpServletResponse.SC_NOT_FOUND, "處理器類不存在: " + className);
} catch (NoSuchMethodException e) {
resp.sendError(HttpServletResponse.SC_NOT_FOUND, "方法不存在或簽名不匹配: " + methodName);
} catch (InvocationTargetException e) {
// 解包業務異常
Throwable cause = e.getCause();
log.error("業務處理異常", cause);
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "業務異常: " + cause.getMessage());
} catch (Exception e) {
log.error("反射調用異常", e);
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "系統異常");
}
}
}
public class UserHandler {
public void login(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
String username = req.getParameter("username");
String password = req.getParameter("password");
// 業務邏輯...
resp.getWriter().write("登錄處理: " + username);
}
public void register(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
// 註冊邏輯...
resp.getWriter().write("註冊處理");
}
}
dispatcher
com.example.DispatcherServlet
1
dispatcher
/api/*
GET /api/dispatcher?className=com.example.UserHandler&method=login&username=zhangsan&password=123456
POST /api/dispatcher?className=com.example.UserHandler&method=register
| API | 用途 | 關鍵點 |
|---|---|---|
Class.forName() | 類加載 | 觸發靜態初始化塊,雙親委派 |
clazz.getDeclaredMethod() | 獲取聲明方法(含 private) | 需精確匹配參數類型順序 |
clazz.getMethod() | 獲取 public 方法(含繼承) | 只能獲取 public |
clazz.getDeclaredConstructor() | 獲取構造器 | 需無參或匹配參數 |
constructor.newInstance() | 創建實例 | 拋出即時異常需解包 |
method.invoke(obj, args) | 調用方法 | 靜態方法 obj 為 null |
method.setAccessible(true) | 暴力反射訪問私有成員 | 繞過訪問檢查,慎用 |
className=java.lang.Runtime 執行系統命令setAccessible(true) 破壞封裝
// 白名單校驗
private static final Set ALLOWED_HANDLERS = Set.of(
"com.example.UserHandler", "com.example.OrderHandler"
);
if (!ALLOWED_HANDLERS.contains(className)) {
throw new SecurityException("非法處理器類: " + className);
}
// 方法名白名單/正則校驗
if (!methodName.matches("^[a-zA-Z][a-zA-Z0-9_]*$")) {
throw new SecurityException("非法方法名: " + methodName);
}
// 禁用暴力反射(安全管理器或模塊化封裝)
public class ReflectionInvoker {
private static final ConcurrentMap METHOD_CACHE = new ConcurrentHashMap<>();
private static final ConcurrentMap, Constructor>> CONSTRUCTOR_CACHE = new ConcurrentHashMap<>();
public static Object invoke(String className, String methodName, Object... args)
throws Exception {
Class> clazz = Class.forName(className);
// 緩存構造器
Constructor> constructor = CONSTRUCTOR_CACHE.computeIfAbsent(clazz, c ->
c.getDeclaredConstructor());
constructor.setAccessible(true);
Object instance = constructor.newInstance();
// 緩存方法(Key: 類名#方法名#參數類型簽名)
String methodKey = className + "#" + methodName + "#" + Arrays.toString(args);
Method method = METHOD_CACHE.computeIfAbsent(methodKey, k -> {
Class>[] paramTypes = Arrays.stream(args).map(Object::getClass).toArray(Class[]::new);
Method m = clazz.getDeclaredMethod(methodName, paramTypes);
m.setAccessible(true);
return m;
});
return method.invoke(instance, args);
}
}
| 方案 | 原理 | 優勢 | 適用場景 |
|---|---|---|---|
| Spring MVC | 註解掃描 + 處理器映射 | 類型安全、聲明式、生態完善 | 企業級 Web 應用 |
| MethodHandle (JDK 7+) | 動態方法句柄 | 比反射快、可內聯優化 | 高效能動態調用 |
| VarHandle (JDK 9+) | 變量句柄 | 字段/數組訪問優化 | 底層庫開發 |
| GraalVM Native Image | AOT 編譯 | 啟動快、無反射開銷 | 雲原生/Serverless |
反射機制賦予 Java 動態性,是框架級開發的基石。本文實現的分發器模式展示了核心流程,但生產環境務必添加白名單校驗、緩存優化、異常解包。現代開發首選成熟框架(Spring MVC、JAX-RS),僅在框架擴展、插件系統、動態代理等底層場景直接使用反射。理解反射原理有助於排查框架啟動慢、類加載衝突、安全漏洞等疑難雜症。