目录
一、反射机制:动态操控类的艺术
1. 反射核心原理
// 获取Class对象的三种方式
Class<?> clazz1 = "hello".getClass(); // 通过对象实例
Class<?> clazz2 = String.class; // 通过类字面量
Class<?> clazz3 = Class.forName("java.lang.String"); // 通过全类名(最常用)
核心能力:
运行时获取类元信息(字段/方法/构造器)
动态创建对象实例
操作私有成员(突破访问限制)
动态调用方法
2. 反射操作全流程
// 1. 获取Class对象
Class<?> userClass = Class.forName("com.example.User");
// 2. 创建实例(默认构造器)
Object user = userClass.newInstance();
// 3. 获取私有字段并修改值
Field nameField = userClass.getDeclaredField("name");
nameField.setAccessible(true); // 突破访问限制
nameField.set(user, "反射修改");
// 4. 调用方法
Method sayHello = userClass.getMethod("sayHello");
sayHello.invoke(user);
3. 五大经典陷阱
陷阱1:泛型检查绕过
List<Integer> list = new ArrayList<>();
list.add(1);
Method addMethod = list.getClass().getMethod("add", Object.class);
addMethod.invoke(list, "字符串"); // 成功插入非Integer类型
int num = list.get(1); // 运行时抛出ClassCastException
原理:泛型类型擦除后,反射操作在运行时无类型约束
陷阱2:性能黑洞
// 反例:高频反射调用
for(int i=0; i<100000; i++){
Method method = target.getClass().getMethod("process");
method.invoke(target); // 每次获取Method对象
}
// 正例:缓存反射对象
Method cachedMethod = target.getClass().getMethod("process");
for(int i=0; i<100000; i++){
cachedMethod.invoke(target);
}
性能对比(单位:纳秒/操作):
操作类型 | 直接调用 | 反射(无缓存) | 反射(有缓存) |
---|---|---|---|
方法调用 | 3 | 2500 | 15 |
陷阱3:破坏单例模式
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return INSTANCE;
}
}
// 反射攻击
Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();
constructor.setAccessible(true);
Singleton fakeInstance = constructor.newInstance(); // 创建第二个实例
防御方案:
private Singleton() {
if(INSTANCE != null) {
throw new IllegalStateException("单例已被创建");
}
}
陷阱4:模块系统限制(Java 9+)
// 未开放模块的私有类
Class<?> clazz = Class.forName("jdk.internal.misc.Unsafe");
// 错误信息:
// Unable to make field private static jdk.internal.misc.Unsafe jdk.internal.misc.Unsafe.theUnsafe accessible:
// module java.base does not "opens jdk.internal.misc" to unnamed module @3b6eb2ec
解决方案:添加JVM参数 --add-opens
开放模块权限
陷阱5:错误处理缺失
try {
Method method = target.getClass().getMethod("notExistMethod");
method.invoke(target);
} catch (NoSuchMethodException e) {
// 必须处理反射查找失败的情况
System.err.println("方法不存在: " + e.getMessage());
}
二、代理模式:控制访问的智慧
1. 静态代理实现
interface Database {
void query(String sql);
}
class MySQL implements Database {
public void query(String sql) {
System.out.println("执行查询: " + sql);
}
}
class LogProxy implements Database {
private Database target;
public LogProxy(Database target) {
this.target = target;
}
public void query(String sql) {
long start = System.nanoTime();
target.query(sql);
System.out.println("耗时: " + (System.nanoTime()-start) + "ns");
}
}
缺点:接口新增方法时,需要同步修改代理类
2. JDK动态代理
class LogHandler implements InvocationHandler {
private final Object target;
public LogHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long start = System.nanoTime();
Object result = method.invoke(target, args);
System.out.println(method.getName() + "耗时: " + (System.nanoTime()-start) + "ns");
return result;
}
}
Database proxy = (Database) Proxy.newProxyInstance(
ClassLoader.getSystemClassLoader(),
new Class[]{Database.class},
new LogHandler(new MySQL())
);
限制:只能代理接口,要求目标类必须实现接口
3. CGLIB动态代理
class UserService {
public void save() {
System.out.println("保存用户");
}
}
class LogInterceptor implements MethodInterceptor {
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
long start = System.nanoTime();
Object result = proxy.invokeSuper(obj, args);
System.out.println(method.getName() + "耗时: " + (System.nanoTime()-start) + "ns");
return result;
}
}
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class);
enhancer.setCallback(new LogInterceptor());
UserService proxy = (UserService) enhancer.create();
特点:
通过继承实现代理
无法代理final类和方法
需要引入第三方库
4. 四大核心陷阱
陷阱1:错误处理导致异常丢失
// 错误示例:吞没异常
public Object invoke(...) {
try {
return method.invoke(target, args);
} catch (Exception e) {
return null; // 异常信息丢失
}
}
// 正确处理
public Object invoke(...) throws Throwable {
try {
return method.invoke(target, args);
} catch (InvocationTargetException e) {
throw e.getTargetException(); // 抛出原始异常
}
}
陷阱2:循环调用问题
public Object invoke(...) {
// 错误:通过proxy对象调用方法导致递归
return method.invoke(proxy, args);
// 正确:调用原始对象方法
return method.invoke(target, args);
}
陷阱3:equals/hashCode处理
// 代理对象的equals可能不符合预期
Database proxy1 = createProxy();
Database proxy2 = createProxy();
System.out.println(proxy1.equals(proxy2)); // 可能返回false
解决方案:在InvocationHandler中重写equals逻辑
陷阱4:CGLIB代理final方法
class UserService {
public final void audit() {
System.out.println("最终审核");
}
}
// 生成代理类时会抛出异常:
// Cannot subclass final class com.example.UserService
三、反射与代理对比分析
维度 | 反射机制 | 代理模式 |
---|---|---|
主要目的 | 运行时操作类元数据 | 控制对象访问,增强功能 |
性能开销 | 较高(需安全检查) | 动态代理首次生成字节码较慢 |
典型应用 | 框架配置/序列化 | AOP实现/远程调用 |
安全性 | 可能破坏封装性 | 隐藏真实对象 |
复杂度 | 直接操作底层API | 抽象层次更高 |
设计模式 | 无特定模式 | 代理模式/装饰器模式 |
四、最佳实践指南
1. 反射安全策略
限制反射权限(使用SecurityManager)
缓存反射元数据(Method/Field对象)
使用
setAccessible
后及时恢复访问状态
Field field = clazz.getDeclaredField("secret");
field.setAccessible(true);
// 操作字段...
field.setAccessible(false); // 恢复访问限制
2. 代理优化方案
对高频代理对象进行缓存
优先使用JDK动态代理(性能更优)
使用混合代理策略(Spring AOP模式)
// Spring的代理选择策略
if(target instanceof Interface) {
return JDK_PROXY;
} else {
return CGLIB_PROXY;
}
3. 现代框架应用
Spring AOP:基于代理的切面编程
MyBatis:Mapper接口的JDK动态代理
Hibernate:延迟加载的代理实现
Mockito:测试Mock对象的动态代理
五、高频面试题解析
1. 反射获取构造器实例化对象
Class<?> clazz = Class.forName("com.example.User");
Constructor<?> constructor = clazz.getDeclaredConstructor(String.class);
constructor.setAccessible(true);
Object instance = constructor.newInstance("testUser");
2. 动态代理实现原理
// JDK动态代理生成的类结构
public final class $Proxy0 extends Proxy implements Database {
private static Method m3;
static {
m3 = Class.forName("com.example.Database").getMethod("query", String.class);
}
public $Proxy0(InvocationHandler h) {
super(h);
}
public final void query(String var1) {
super.h.invoke(this, m3, new Object[]{var1});
}
}
3. CGLIB与JDK代理性能对比
操作次数 | JDK代理耗时(ms) | CGLIB代理耗时(ms) |
---|---|---|
1,000 | 12 | 45 |
100,000 | 15 | 60 |
1,000,000 | 120 | 550 |
(测试环境:JDK 17,启用反射优化参数)
深度总结
反射的本质:突破静态类型系统的限制,在运行时动态操作类和对象,为框架开发提供基础能力,但需要谨慎处理安全和性能问题。
代理模式的价值:通过中间层控制对象访问,实现功能增强和系统解耦,是现代框架设计的核心模式之一。
避坑关键点:
反射操作需处理安全检查异常(SecurityException)
动态代理方法调用注意异常传播
CGLIB无法代理final方法和类
代理对象的equals/hashCode需特殊处理
反射性能优化依赖元数据缓存
建议在IDE中开启以下检测:
反射API使用警告检查
代理类生成配置优化
final方法代理错误检测
动态代理接口合规性验证
通过合理运用反射和代理机制,结合防御性编程思维,能够构建出灵活强大的Java应用程序。理解这些原理也有助于深入掌握Spring等主流框架的工作机制。