Java 反射 (java.lang.reflect) 允許運行中檢查類結構、實例化物件、調用方法、訪問字段、處理註解。Spring IoC、Hibernate ORM、MyBatis Mapper、JUnit 測試框架、動態代理 AOP 等所有主流框架的核心均建立於反射之上。本文從基礎 API 入手,逐步構建一個完整的動態代理 AOP 攔截器框架,並展示註解掃描與 WebService 發布實戰。
| 任務 | 關鍵類/方法 | 備註 |
|---|---|---|
| 獲取 Class 物件 | Class.forName()、.class、obj.getClass() | 每類型僅一個 Class 實例 |
| 掃描構造器 | getConstructor()、getDeclaredConstructors() | 前者含繼承/public,後者含私有/本類聲明 |
| 掃描字段 | getField()、getDeclaredFields() | 同理,setAccessible(true) 破壞封裝 |
| 掃描方法 | getMethod()、getDeclaredMethods() | 參數型別陣列精確匹配 |
| 實例化 | clazz.newInstance() (Deprecated)、constructor.newInstance() | 推薦構造器方式,支援帶參 |
| 調用方法 | method.invoke(obj, args...) | 靜態方法傳 null |
| 讀寫字段 | field.get(obj)、field.set(obj, val) | 需 setAccessible(true) |
| 讀取註解 | getAnnotation()、getAnnotations()、isAnnotationPresent() | 需 @Retention(RUNTIME) |
interface HelloWorldService { void sayHello(String name); }
class MyHelloWorld implements HelloWorldService {
public String name; // 故意用 public 字段演示
public void sayHello(String name) { System.out.println("Hello " + name + "."); }
public void setName(String name) { this.name = name; }
public String getName() { return name; }
}
Class> clazz = Class.forName("sample.reflection.MyHelloWorld");
Arrays.stream(clazz.getDeclaredMethods()).forEach(m -> System.out.println(m.toGenericString()));
Arrays.stream(clazz.getFields()).forEach(f -> System.out.println(f.toGenericString()));
/* 輸出:
public void sample.reflection.MyHelloWorld.sayHello(java.lang.String)
public java.lang.String sample.reflection.MyHelloWorld.getName()
public void sample.reflection.MyHelloWorld.setName(java.lang.String)
public java.lang.String sample.reflection.MyHelloWorld.name
*/
MyHelloWorld hello = (MyHelloWorld) clazz.getDeclaredConstructor().newInstance();
Method sayHello = clazz.getMethod("sayHello", String.class);
sayHello.invoke(hello, "Zhang San"); // Hello Zhang San.
Field nameField = clazz.getDeclaredField("name");
nameField.setAccessible(true);
System.out.println("Before: " + nameField.get(hello)); // null
nameField.set(hello, "Zhang San");
System.out.println("After: " + nameField.get(hello)); // Zhang San
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented
@interface ClassAnnotation { String value(); }
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented
@interface MethodAnnotation { String methodName(); String returnType(); }
@Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME) @Documented
@interface ParameterAnnotation { String value(); }
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @Documented
@interface FieldAnnotation { String value(); }
@ClassAnnotation("類級註解")
class MyClass {
@MethodAnnotation(methodName="printInfo", returnType="void")
public void printInfo(String info) { System.out.println(info); }
@MethodAnnotation(methodName="printError", returnType="void")
public void printError(@ParameterAnnotation("參數註解") String error) {
System.err.println(error);
}
@FieldAnnotation("字段註解")
public int count;
}
void scanAll(MyClass target) {
// 類註解
Arrays.stream(target.getClass().getAnnotations()).forEach(System.out::println);
// 方法與參數註解
for (Method m : target.getClass().getDeclaredMethods()) {
Arrays.stream(m.getAnnotations()).forEach(System.out::println);
for (Annotation[] pa : m.getParameterAnnotations())
Arrays.stream(pa).forEach(System.out::println);
}
// 字段註解
Arrays.stream(target.getClass().getFields())
.flatMap(f -> Arrays.stream(f.getAnnotations()))
.forEach(System.out::println);
}
void scanPrecise(Class> clazz) {
if (clazz.isAnnotationPresent(ClassAnnotation.class)) {
System.out.println(clazz.getAnnotation(ClassAnnotation.class));
}
for (Method m : clazz.getMethods()) { // 含繼承
if (m.isAnnotationPresent(MethodAnnotation.class)) {
System.out.println(m.getAnnotation(MethodAnnotation.class));
}
for (Annotation[] pa : m.getParameterAnnotations())
for (Annotation a : pa) System.out.println(a);
}
for (Field f : clazz.getFields()) {
if (f.isAnnotationPresent(FieldAnnotation.class))
System.out.println(f.getAnnotation(FieldAnnotation.class));
}
}
@WebService(targetNamespace="https://test", serviceName="HelloService")
public class HelloServiceProvider {
@WebResult(name="HelloString")
@WebMethod
public String sayHello(@WebParam(name="userName") String name) {
return "Hello " + name;
}
@Oneway @WebMethod(action="userLogin", operationName="userLogin")
public void login() { System.out.println("User has logged on."); }
public static void main(String[] args) {
// JDK 內建輕量級 HTTP 伺服器發布
Endpoint.publish("https://localhost:8888/test/HelloService", new HelloServiceProvider());
}
}
// 瀏覽器訪問 https://localhost:8888/test/HelloService?wsdl 獲取 WSDL
interface AOPInterceptor {
void before(Method method, Object[] args);
void after(Method method, Object[] args);
void afterThrowing(Method method, Object[] args);
void afterFinally(Method method, Object[] args);
}
class DynamicProxyInvocationHandler implements InvocationHandler {
private final Object target;
private final AOPInterceptor interceptor;
public DynamicProxyInvocationHandler(Object target, AOPInterceptor interceptor) {
this.target = target;
this.interceptor = interceptor;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
interceptor.before(method, args);
Object result = method.invoke(target, args); // 執行目標方法
interceptor.after(method, args);
return result;
} catch (Throwable t) {
interceptor.afterThrowing(method, args);
throw t; // 保持異常拋出
} finally {
interceptor.afterFinally(method, args);
}
}
}
interface DynamicProxyFactory {
T createProxy(Class clazz, T target, AOPInterceptor interceptor);
}
class DynamicProxyFactoryImpl implements DynamicProxyFactory {
@Override
public T createProxy(Class clazz, T target, AOPInterceptor interceptor) {
InvocationHandler handler = new DynamicProxyInvocationHandler(target, interceptor);
// 關鍵:Proxy.newProxyInstance(類加載器, 實現介面陣列, handler)
return (T) Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),
new Class>[] { clazz },
handler
);
}
}
class MyInterceptor implements AOPInterceptor {
public void before(Method m, Object[] args) { System.out.println("方法開始執行: " + m.getName()); }
public void after(Method m, Object[] args) { System.out.println("方法執行結束: " + m.getName()); }
public void afterThrowing(Method m, Object[] args) { System.out.println("方法拋出異常: " + m.getName()); }
public void afterFinally(Method m, Object[] args) { System.out.println("方法 finally 執行: " + m.getName()); }
}
public static void main(String[] args) {
HelloWorldService target = new MyHelloWorld(); // 目標物件
DynamicProxyFactory factory = new DynamicProxyFactoryImpl();
HelloWorldService proxy = factory.createProxy(HelloWorldService.class, target, new MyInterceptor());
proxy.sayHello("Zhang San"); // 正常流程
// 輸出:
// 方法開始執行: sayHello
// Hello Zhang San.
// 方法執行結束: sayHello
// 方法 finally 執行: sayHello
// 測試異常流程 (在 sayHello 內拋出 RuntimeException)
// 輸出:
// 方法開始執行: sayHello
// 方法拋出異常: sayHello
// 方法 finally 執行: sayHello
// Exception in thread "main" java.lang.reflect.UndeclaredThrowableException ...
}
| 優化點 | 建議 |
|---|---|
| 緩存 Class/Method/Field | 啟動時掃描緩存至 Map,避免重複 getMethod() 解析字節碼 |
| setAccessible(true) | 一次性設置,JIT 可優化訪問檢查;注意 Java 9+ 模組系統需 --add-opens |
| 優先用 MethodHandle / VarHandle | Java 7+ MethodHandles.lookup() 直接生成調用句柄,效能接近直接調用 |
| 避免熱點路徑反射 | 高頻調用路徑改用代碼生成 (ASM/ByteBuddy) 或 Lambda 元工廠 |
| 模組化封裝 | Java 9+ 需在 module-info.java 中 opens com.pkg to java.base 允許深度反射 |
反射賦予了 Java 「運行時自我認知與修改」的動態能力,是框架級開發的基石。掌握 Class、Method、Field、Constructor、Annotation、Proxy 六大核心類,並理解 InvocationHandler 動態代理機制,即可構建 IoC 容器、AOP 框架、ORM 映射、RPC 框架等核心基礎設施。實戰中務必關注緩存優化、模組系統相容性、安全權限,並在極致效能場景考慮 MethodHandle 或字節碼生成替代方案。