反射是 Java 動態性的基石,支撐 Spring IOC、MyBatis 映射、Jackson 序列化等所有主流框架。本文系統梳理核心 API、訪問控制繞過、性能優化、實戰模式。
java.lang.Class // 入口:類元數據
getFields() / getDeclaredFields() // Field[]
getMethods() / getDeclaredMethods() // Method[]
getConstructors() / getDeclaredConstructors() // Constructor[]
getSuperclass() / getInterfaces() // 繼承關係
getAnnotations() / getDeclaredAnnotations() // 註解
newInstance() / getDeclaredConstructor().newInstance() // 實例化
java.lang.reflect.Field // 字段操作
getType() / getGenericType()
get(Object) / set(Object, Object) // 讀寫值
setAccessible(true) // 繞過 private
java.lang.reflect.Method // 方法操作
getParameterTypes() / getGenericParameterTypes()
getReturnType() / getGenericReturnType()
getExceptionTypes()
invoke(Object, Object...) // 動態調用
setAccessible(true)
java.lang.reflect.Constructor // 構造器操作
newInstance(Object...) // 創建實例
setAccessible(true)
Field f = Target.class.getDeclaredField("secret");
f.setAccessible(true); // 關閉訪問檢查
Object value = f.get(targetInstance);
// JDK 9+ 模塊系統限制:需在 module-info.java 開放
// module my.app { opens com.my.internal to java.base; }
// 或啟動參數 --add-opens java.base/java.lang=ALL-UNNAMED
風險:破壞封裝、繞過安全管理器、JDK 17+ 默認強封裝需額外配置。
public static void copyProperties(Object source, Object target) {
Map targetFields = Arrays.stream(target.getClass().getDeclaredFields())
.collect(Collectors.toMap(Field::getName, f -> { f.setAccessible(true); return f; }));
for (Field sf : source.getClass().getDeclaredFields()) {
sf.setAccessible(true);
Field tf = targetFields.get(sf.getName());
if (tf != null && tf.getType().isAssignableFrom(sf.getType())) {
tf.set(target, sf.get(source));
}
}
}
@Retention(RUNTIME) @Target(FIELD)
public @interface ConfigValue { String key() default ""; }
public static void bindConfig(Object bean, ConfigSource source) {
for (Field f : bean.getClass().getDeclaredFields()) {
ConfigValue ann = f.getAnnotation(ConfigValue.class);
if (ann != null) {
f.setAccessible(true);
String key = ann.key().isEmpty() ? f.getName() : ann.key();
String val = source.getProperty(key);
if (val != null) convertAndSet(f, bean, val); // 類型轉換
}
}
}
public class LoggingProxy implements InvocationHandler {
private final Object target;
public static Object newInstance(Object target) {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new LoggingProxy(target));
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long start = System.nanoTime();
try {
return method.invoke(target, args);
} finally {
log.info("{}#{} took {} ms", target.getClass().getSimpleName(),
method.getName(), (System.nanoTime() - start) / 1e6);
}
}
}
public class Reflect {
private final Class> clazz;
public static Reflect on(Class> clazz) { return new Reflect(clazz); }
public static Reflect on(String className) {
try { return on(Class.forName(className)); }
catch (ClassNotFoundException e) { throw new IllegalArgumentException(e); }
}
public Reflect create(Object... args) {
try {
Constructor> c = Arrays.stream(clazz.getDeclaredConstructors())
.filter(cn -> cn.getParameterCount() == args.length)
.findFirst().orElseThrow(() -> new IllegalStateException("No matching constructor"));
c.setAccessible(true);
this.instance = c.newInstance(args);
return this;
} catch (Exception e) { throw new RuntimeException(e); }
}
public Reflect set(String fieldName, Object value) {
try {
Field f = clazz.getDeclaredField(fieldName);
f.setAccessible(true);
f.set(instance, value);
return this;
} catch (Exception e) { throw new RuntimeException(e); }
}
public T call(String methodName, Object... args) {
try {
Method m = Arrays.stream(clazz.getDeclaredMethods())
.filter(mn -> mn.getName().equals(methodName) && mn.getParameterCount() == args.length)
.findFirst().orElseThrow(() -> new IllegalStateException("No matching method"));
m.setAccessible(true);
return (T) m.invoke(instance, args);
} catch (Exception e) { throw new RuntimeException(e); }
}
public Object get() { return instance; }
private Object instance;
}
// 使用
User user = Reflect.on(User.class).create().set("name", "Tom").set("age", 18).call("init").get();
| 策略 | 原理 | 適用場景 |
|---|---|---|
| 緩存 Member 對象 | Field/Method/Constructor 對象創建昂貴,靜態緩存復用 | 高頻調用路徑 |
| MethodHandle (JDK 7+) | 直接方法句柄,JIT 內聯優化優於 reflect | 熱點路徑、框架開發 |
| VarHandle (JDK 9+) | 字段/數組元素訪問句柄,支援原子操作 | 並發容器、非阻塞算法 |
| 字節碼生成 (ASM/ByteBuddy) | 運行時生成專用類,消除反射開銷 | 極致性能、ORM、序列化 |
| 避免 setAccessible | 破壞 JIT 優化假設,模塊系統限制 | 優先設計 public API |
// 反射調用
Method m = Service.class.getMethod("process", Request.class);
m.invoke(service, request);
// MethodHandle 等效
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodType mt = MethodType.methodType(void.class, Request.class);
MethodHandle mh = lookup.findVirtual(Service.class, "process", mt);
mh.invokeExact(service, request); // 簽名精確匹配,性能更優