Java 反射 API (java.lang.reflect.* + java.lang.Class) 讓程式在運行時獲得「自我認知」能力:檢查類結構、創建實例、調用方法、訪問字段、處理註解。Spring IoC 容器、Hibernate ORM、MyBatis Mapper 代理、JUnit 測試引擎、RPC 框架 (Dubbo/gRPC) 等所有主流框架的核心均建立於反射之上。本文從 JVM 層面解析 Class 物件本質,系統講解核心 API、實戰模式、動態代理 AOP 構建、註解驅動開發與效能優化最佳實踐。
// 1. 對象實例獲取 (最常用)
Class> c1 = "hello".getClass();
// 2. 類字面量 (編譯期已知,無拋出異常)
Class c2 = String.class;
// 3. 字串全限定名 (動態載入,配置驅動核心)
Class> c3 = Class.forName("java.lang.String");
// 4. 基本型別包裝類 .TYPE
Class c4 = Integer.TYPE; // 等同於 int.class
// 驗證:同一類型在同一類加載器下 Class 實例唯一
System.out.println(c1 == c2); // true
System.out.println(c3 == c4); // true (int/Integer.TYPE)
Class 物件於方法區 (Metaspace)。<clinit> (靜態塊/靜態字段賦值)。Class 物件。關鍵點:Class.forName() 會觸發初始化;ClassLoader.loadClass() 預設不初始化。
| 分類 | 獲取單個 (精確匹配) | 獲取所有 (批量掃描) | 權限差異 |
|---|---|---|---|
| 構造器 | getConstructor(Class... params) |
getConstructors() |
getX():含繼承、僅 publicgetDeclaredX():本類聲明、含私有、不含繼承 |
| 方法 | getMethod(name, Class... params) |
getMethods() |
|
| 字段 | getField(name) |
getFields() |
|
| 聲明構造器 | getDeclaredConstructor(Class... params) |
getDeclaredConstructors() |
同左,但可訪問 private |
| 聲明方法 | getDeclaredMethod(name, Class... params) |
getDeclaredMethods() |
同上 |
| 聲明字段 | getDeclaredField(name) |
getDeclaredFields() |
同上 |
getDeclaredX() + setAccessible(true) (業務字段多為 private)。getMethod()/getConstructor() (支援繼承、語義清晰)。newInstance() 已棄用 (Java 9) → 改用 constructor.newInstance(args...)。Class> clazz = Class.forName("com.example.User");
// 無參構造
User u1 = (User) clazz.getDeclaredConstructor().newInstance();
// 帶參構造 (精確匹配參數型別)
Constructor> ctor = clazz.getDeclaredConstructor(int.class, String.class, int.class);
User u2 = (User) ctor.newInstance(1001, "zhangsan", 25);
Method setName = clazz.getMethod("setName", String.class); // 含繼承、public
setName.invoke(u1, "lisi"); // 靜態方法傳 null
// 可變參數方法調用
Method printAll = clazz.getMethod("printAll", String[].class);
printAll.invoke(u1, (Object) new String[]{"a", "b", "c"}); // 注意陣列包裝
Field nameField = clazz.getDeclaredField("name");
nameField.setAccessible(true); // 關鍵:暴力反射,繞過 private/modifiers
System.out.println(nameField.get(u1)); // 讀取
nameField.set(u1, "wangwu"); // 寫入
// 基本型別字段自動拆箱/裝箱
Field ageField = clazz.getDeclaredField("age");
ageField.setInt(u1, 30); // 專用 setInt/getInt 避免裝箱開銷
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented
@interface Table { String value(); }
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @Documented
@Column String value(); boolean primaryKey() default false; }
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented
@interface Transient { }
String buildCreateTableSQL(Class> entityClass) {
Table tableAnn = entityClass.getAnnotation(Table.class);
if (tableAnn == null) throw new IllegalArgumentException("缺少 @Table");
StringBuilder sql = new StringBuilder("CREATE TABLE ").append(tableAnn.value()).append(" (");
List columns = new ArrayList<>();
List pkColumns = new ArrayList<>();
for (Field f : entityClass.getDeclaredFields()) {
if (f.isAnnotationPresent(Transient.class)) continue; // 忽略
Column colAnn = f.getAnnotation(Column.class);
if (colAnn == null) continue;
String colDef = colAnn.value() + " " + mapSqlType(f.getType());
if (colAnn.primaryKey()) pkColumns.add(colAnn.value());
columns.add(colDef);
}
sql.append(String.join(", ", columns));
if (!pkColumns.isEmpty()) sql.append(", PRIMARY KEY (").append(String.join(",", pkColumns)).append(")");
sql.append(");");
return sql.toString();
}
// 測試
System.out.println(buildCreateTableSQL(User.class));
// 輸出: CREATE TABLE t_user (id BIGINT, username VARCHAR, email VARCHAR, PRIMARY KEY (id));
InvocationHandler:代理邏輯核心,攔截所有介面方法調用。Proxy.newProxyInstance(loader, interfaces, handler):生成實現指定介面的代理類實例。// 1. 攔截器契約
interface Interceptor {
void before(Method method, Object[] args);
Object after(Method method, Object[] args, Object result);
void afterThrowing(Method method, Object[] args, Throwable t);
void afterFinally(Method method, Object[] args);
}
// 2. 通用 InvocationHandler
class AopInvocationHandler implements InvocationHandler {
private final Object target;
private final List interceptors;
public AopInvocationHandler(Object target, List interceptors) {
this.target = target;
this.interceptors = interceptors;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 前置通知
for (Interceptor ic : interceptors) ic.before(method, args);
try {
Object result = method.invoke(target, args); // 執行目標
// 後置通知 (可修改返回值)
for (Interceptor ic : interceptors) result = ic.after(method, args, result);
return result;
} catch (Throwable t) {
for (Interceptor ic : interceptors) ic.afterThrowing(method, args, t);
throw t;
} finally {
for (Interceptor ic : interceptors) ic.afterFinally(method, args);
}
}
}
// 3. 代理工廠
class ProxyFactory {
@SuppressWarnings("unchecked")
public static T createProxy(Class interfaceClass, T target, Interceptor... interceptors) {
return (T) Proxy.newProxyInstance(
interfaceClass.getClassLoader(),
new Class>[]{interfaceClass},
new AopInvocationHandler(target, Arrays.asList(interceptors))
);
}
}
// 日誌攔截器
class LoggingInterceptor implements Interceptor {
private static final Logger log = LoggerFactory.getLogger(LoggingInterceptor.class);
public void before(Method m, Object[] args) { log.debug("ENTER {} args={}", m.getName(), Arrays.toString(args)); }
public Object after(Method m, Object[] args, Object r) { log.debug("EXIT {} result={}", m.getName(), r); return r; }
public void afterThrowing(Method m, Object[] args, Throwable t) { log.error("EXCEPTION {} ", m.getName(), t); }
public void afterFinally(Method m, Object[] args) {}
}
// 效能監控攔截器
class MetricsInterceptor implements Interceptor {
private final MeterRegistry registry;
public MetricsInterceptor(MeterRegistry registry) { this.registry = registry; }
public void before(Method m, Object[] args) { /* 記錄開始時間 ThreadLocal */ }
public Object after(Method m, Object[] args, Object r) {
Timer.Sample sample = (Timer.Sample) ThreadLocalHolder.get();
sample.stop(Timer.builder("method.latency").tag("method", m.getName()).register(registry));
return r;
}
public void afterThrowing(Method m, Object[] args, Throwable t) {}
public void afterFinally(Method m, Object[] args) {}
}
// 使用
UserService target = new UserServiceImpl();
UserService proxy = ProxyFactory.createProxy(UserService.class, target,
new LoggingInterceptor(), new MetricsMetricsInterceptor(registry));
proxy.saveUser(new User()); // 自動織入日誌、監控
modifiers、accessible、安全管理器。invoke(obj, args...) 需構建 Object[]、基本型別裝箱。getMethod() 遍歷方法表、字串比對、參數型別匹配。// 直接調用: ~1 ns
// Method.invoke: ~50-100 ns (開啟 setAccessible 後)
// 反射調用約 50-100 倍開銷,但絕對值仍極小
| 優化手段 | 原理 | 適用場景 |
|---|---|---|
| 緩存 Member 物件 | 啟動掃描緩存 Method/Field/Constructor 至 ConcurrentHashMap |
所有反射場景,避免重複解析 |
| setAccessible(true) | 關閉訪問檢查,JIT 可內聯 | 私有成員訪問,框架初始化時一次性設置 |
| MethodHandle / VarHandle | Java 7+ MethodHandles.lookup() 直接生成調用句柄,效能接近直接調用 |
高頻熱點路徑 (如序列化、ORM 映射) |
| Lambda 元工廠 | Java 8+ LambdaMetafactory.metafactory() 動態生成函數式介面實現 |
將反射調用轉為 Lambda 調用 |
| 字節碼生成 | ASM/ByteBuddy 生成專用存取類,完全消除反射 | 極致效能場景 (如 JSON 庫、ORM) |
| 模組系統開放 | Java 9+ module-info.java: opens com.pkg to java.base |
模組化項目深度反射必配 |
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle getter = lookup.findGetter(User.class, "name", String.class); // 直接字段訪問
MethodHandle setter = lookup.findSetter(User.class, "name", String.class);
MethodHandle sayHello = lookup.findVirtual(User.class, "sayHello", MethodType.methodType(void.class, String.class));
User u = new User();
setter.invokeExact(u, "Zhang San");
System.out.println(getter.invokeExact(u)); // Zhang San
sayHello.invokeExact(u, "World"); // Hello World.
// 效能: 僅比直接調用慢 1-2 倍,遠優於 Method.invoke
Class 是 JVM 運行時類型元數據入口,反射是動態訪問元數據的標準 API。Class、Constructor、Method、Field、Annotation、Proxy 六大類。getDeclaredX()+緩存;運行時調用用 getMethod()+MethodHandle。反射是框架作者的利器,也是業務開發者的陷阱。善用封裝,隔離複雜性,讓業務代碼保持簡潔、類型安全、高效能。