反射
关于反射的详细解读,请看这篇文章 Java 反射机制详解 。
什么是反射?
简单来说,Java 反射 (Reflection) 是一种在程序运行时,动态地获取类的信息并操作类或对象(方法、属性)的能力。
通常情况下,我们写的代码在编译时类型就已经确定了,要调用哪个方法、访问哪个字段都是明确的。但反射允许我们在运行时才去探知一个类有哪些方法、哪些属性、它的构造函数是怎样的,甚至可以动态地创建对象、调用方法或修改属性,哪怕这些方法或属性是私有的。
正是这种在运行时“反观自身”并进行操作的能力,使得反射成为许多通用框架和库的基石。它让代码更加灵活,能够处理在编译时未知的类型。
反射有什么优缺点?
优点:
- 灵活性和动态性:反射允许程序在运行时动态地加载类、创建对象、调用方法和访问字段。这样可以根据实际需求(如配置文件、用户输入、注解等)动态地适应和扩展程序的行为,显著提高了系统的灵活性和适应性。
- 框架开发的基础:许多现代 Java 框架(如 Spring、Hibernate、MyBatis)都大量使用反射来实现依赖注入(DI)、面向切面编程(AOP)、对象关系映射(ORM)、注解处理等核心功能。反射是实现这些“魔法”功能不可或缺的基础工具。
- 解耦合和通用性:通过反射,可以编写更通用、可重用和高度解耦的代码,降低模块之间的依赖。例如,可以通过反射实现通用的对象拷贝、序列化、Bean 工具等。
缺点:
- 性能开销:反射操作通常比直接代码调用要慢。因为涉及到动态类型解析、方法查找以及 JIT 编译器的优化受限等因素。不过,对于大多数框架场景,这种性能损耗通常是可以接受的,或者框架本身会做一些缓存优化。
- 安全性问题:反射可以绕过 Java 语言的访问控制机制(如访问
private字段和方法),破坏了封装性,可能导致数据泄露或程序被恶意篡改。此外,还可以绕过泛型检查,带来类型安全隐患。 - 代码可读性和维护性:过度使用反射会使代码变得复杂、难以理解和调试。错误通常在运行时才会暴露,不像编译期错误那样容易发现。
反射的应用场景?
我们平时写业务代码可能很少直接跟 Java 的反射(Reflection)打交道。但你可能没意识到,你天天都在享受反射带来的便利!很多流行的框架,比如 Spring/Spring Boot、MyBatis 等,底层都大量运用了反射机制,这才让它们能够那么灵活和强大。
下面简单列举几个最场景的场景帮助大家理解。
1.依赖注入与控制反转(IoC)
以 Spring/Spring Boot 为代表的 IoC 框架,会在启动时扫描带有特定注解(如 @Component, @Service, @Repository, @Controller)的类,利用反射实例化对象(Bean),并通过反射注入依赖(如 @Autowired、构造器注入等)。
2.注解处理
注解本身只是个“标记”,得有人去读这个标记才知道要做什么。反射就是那个“读取器”。框架通过反射检查类、方法、字段上有没有特定的注解,然后根据注解信息执行相应的逻辑。比如,看到 @Value,就用反射读取注解内容,去配置文件找对应的值,再用反射把值设置给字段。
3.动态代理与 AOP
想在调用某个方法前后自动加点料(比如打日志、开事务、做权限检查)?AOP(面向切面编程)就是干这个的,而动态代理是实现 AOP 的常用手段。JDK 自带的动态代理(Proxy 和 InvocationHandler)就离不开反射。代理对象在内部调用真实对象的方法时,就是通过反射的 Method.invoke 来完成的。
public class DebugInvocationHandler implements InvocationHandler {
private final Object target; // 真实对象
public DebugInvocationHandler(Object target) { this.target = target; }
// proxy: 代理对象, method: 被调用的方法, args: 方法参数
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("切面逻辑:调用方法 " + method.getName() + " 之前");
// 通过反射调用真实对象的同名方法
Object result = method.invoke(target, args);
System.out.println("切面逻辑:调用方法 " + method.getName() + " 之后");
return result;
}
}4.对象关系映射(ORM)
像 MyBatis、Hibernate 这种框架,能帮你把数据库查出来的一行行数据,自动变成一个个 Java 对象。它是怎么知道数据库字段对应哪个 Java 属性的?还是靠反射。它通过反射获取 Java 类的属性列表,然后把查询结果按名字或配置对应起来,再用反射调用 setter 或直接修改字段值。反过来,保存对象到数据库时,也是用反射读取属性值来拼 SQL。
评论
使用 GitHub 账号即可参与加载较慢?可 直接前往 GitHub Discussions 查看与参与。