Java反射机制是运行时动态获取类信息并操作类成员的能力集合。它打破了"编译时绑定"的限制,允许程序在运行时才确定要操作的类、方法和字段。这种能力看似违背了Java静态类型语言的特性,实则提供了极大的灵活性。
反射的核心价值体现在三个层面:
注意:反射打破了封装性原则,过度使用会导致性能问题和安全隐患。实际开发中需要权衡使用场景。
java复制// 1. 类名.class语法
Class<?> clazz1 = String.class;
// 2. 对象.getClass()
String str = "";
Class<?> clazz2 = str.getClass();
// 3. Class.forName()动态加载
Class<?> clazz3 = Class.forName("java.lang.String");
第三种方式最具动态性,但需要注意:
反射方法调用比直接调用慢50-100倍,主要因为:
实测案例:
java复制// 直接调用
method.invoke(obj); // 平均耗时:120ns
// 反射调用
Method m = clazz.getMethod("method");
m.invoke(obj); // 平均耗时:6500ns
优化方案:
Spring AOP的底层实现原理:
java复制public class DebugProxy implements InvocationHandler {
private Object target;
public static Object newInstance(Object obj) {
return Proxy.newProxyInstance(
obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(),
new DebugProxy(obj));
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) {
System.out.println("Before method: " + method.getName());
Object result = method.invoke(target, args);
System.out.println("After method: " + method.getName());
return result;
}
}
自定义注解的运行时处理示例:
java复制@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation {
String value();
}
class AnnotationProcessor {
public static void process(Object obj) {
for (Method method : obj.getClass().getMethods()) {
if (method.isAnnotationPresent(MyAnnotation.class)) {
MyAnnotation anno = method.getAnnotation(MyAnnotation.class);
System.out.println("Found annotation: " + anno.value());
}
}
}
}
访问私有成员的正确方式:
java复制Field field = clazz.getDeclaredField("privateField");
field.setAccessible(true); // 关键步骤
Object value = field.get(obj);
警告:setAccessible(true)会触发安全管理器检查,在受限环境(如Applet)可能抛出SecurityException
不安全的类型转换:
java复制List list = (List) field.get(obj); // 可能抛出ClassCastException
安全方案:
java复制if (field.getType().isAssignableFrom(List.class)) {
List<?> list = (List<?>) field.get(obj);
}
创建和操作数组:
java复制// 创建String数组
Object array = Array.newInstance(String.class, 10);
// 设置元素
Array.set(array, 0, "first");
// 获取元素
String element = (String) Array.get(array, 0);
典型缓存实现:
java复制public class ReflectCache {
private static final Map<Class<?>, Map<String, Method>> METHOD_CACHE = new ConcurrentHashMap<>();
public static Method getMethod(Class<?> clazz, String name) {
return METHOD_CACHE.computeIfAbsent(clazz, k -> new HashMap<>())
.computeIfAbsent(name, n -> {
try {
return clazz.getMethod(n);
} catch (Exception e) {
throw new RuntimeException(e);
}
});
}
}
Java 9+提供的更优选择:
示例对比:
java复制// 传统反射
Method m = clazz.getMethod("toString");
String result = (String) m.invoke(obj);
// MethodHandle方式
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle mh = lookup.findVirtual(clazz, "toString", MethodType.methodType(String.class));
String result = (String) mh.invoke(obj); // 快3-5倍
常见攻击向量:
防护措施:
java复制SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new ReflectPermission("suppressAccessChecks"));
}
推荐实践:
模块描述示例:
java复制module my.module {
opens com.my.pkg to spring.core;
}
Bean属性注入的核心流程:
关键代码段:
java复制Field[] fields = bean.getClass().getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(Autowired.class)) {
Object dependency = context.getBean(field.getType());
field.setAccessible(true);
field.set(bean, dependency);
}
}
结果集到对象的转换过程:
性能优化点:
| 方案 | 优点 | 缺点 |
|---|---|---|
| ASM | 性能最优 | 学习曲线陡峭 |
| Byte Buddy | API友好 | 体积较大 |
| CGLIB | 功能丰富 | 已停止维护 |
对于需要高度动态性的场景,可以考虑:
反射的JVM实现路径:
现代JVM对反射的优化:
查看Inflation状态:
bash复制-XX:+PrintFlagsFinal | grep Inflation
| 异常类型 | 原因分析 | 解决方案 |
|---|---|---|
| IllegalAccessException | 访问权限不足 | setAccessible(true) |
| IllegalArgumentException | 参数类型不匹配 | 检查方法签名 |
| InvocationTargetException | 被调方法抛出异常 | getCause()查根源 |
分析反射性能瓶颈的命令:
bash复制async-profiler -e reflect.Method.invoke -d 30 -f profile.html pid