反射
反射
70. 什么是反射?
回答:
反射是 Java 提供的一种强大机制,允许程序在运行时动态获取类的结构信息(如字段、方法、构造器等)并进行调用或修改操作。它是实现框架、容器、动态代理等底层机制的关键能力。
分析:
反射(Reflection)机制是 Java 语言的元编程能力,它允许我们在运行时分析一个类的所有属性、方法、注解等结构信息,甚至实例化对象、访问私有成员或调用任意方法。
反射的核心入口是 java.lang.reflect 包,配合 Class 类可以动态实现以下操作:
- 获取类的 Class 对象(如:
Class.forName("com.example.MyClass")) - 获取字段、方法、构造器等结构信息
- 修改字段值(包括私有字段)
- 动态调用方法(包括私有方法)
- 实例化对象(甚至无构造器参与)
反射让代码具备了"动态性"和"自描述性",在不知道类名、方法名等前提下也能完成操作,是构建 IoC 容器、AOP 框架、ORM 映射等功能的基础能力。
尽管反射非常强大,但使用时也要注意权限控制与性能影响。
71. 反射的优缺点?
回答:
反射的优点在于灵活性强,适用于框架和工具开发,但也存在性能开销和类型安全缺失等问题,因此应在必要场景中合理使用。
分析:
反射提供了在运行时动态操作类结构的能力,显著提升了代码的通用性和灵活性,是很多底层框架得以实现的基石。然而,它也不是万能钥匙,使用时需权衡利弊。
优点:
- 高度灵活:不依赖于编译时类型,可在运行时动态操作任意类、字段、方法
- 支持动态扩展:适合插件式架构、动态代理、IOC、ORM 等框架场景
- 提升复用性:同一套代码可操作不同结构的类,简化代码冗余
缺点:
- 性能较差:反射涉及的动态解析、访问控制绕过等操作开销大于普通代码
- 破坏封装:通过反射可访问私有字段与方法,违背 OOP 封装原则
- 类型安全不足:绕过泛型检查,可能在运行时抛出类型转换异常
- 调试困难:错误往往在运行时暴露,堆栈信息不明确,影响排查效率
因此,反射应主要用于框架层开发或必须动态行为的场景,在业务代码中应尽量少用,以提升代码可维护性与性能。
72. 反射的应用场景?
回答:
反射广泛用于各种 Java 框架中,如 Spring Bean 管理、MyBatis 实体映射、JDK 动态代理、注解处理器、SPI 加载机制等,支撑了 Java 生态中众多核心功能。
分析:
虽然在日常业务开发中我们可能很少直接使用反射 API,但其在框架和底层机制中几乎无处不在。以下是典型应用场景:
Spring 框架:
Spring 通过反射实现 Bean 的自动实例化、依赖注入、字段注解识别(如 @Autowired)、AOP 等功能。JDK 动态代理:
基于java.lang.reflect.Proxy和InvocationHandler,JDK 可在运行时生成代理类并通过反射调用目标对象方法,广泛用于事务控制、RPC、权限校验等。注解处理:
反射可读取类、方法、字段上的注解,用于驱动配置和行为控制,如@RequestMapping、@Component等。ORM 框架(如 MyBatis):
反射用于将数据库记录映射为 Java 实体类对象,并支持字段动态赋值、结果集绑定等。SPI 服务发现机制:
JDK 的 ServiceLoader 底层使用反射加载接口实现类,支持运行时的模块扩展。JDBC 驱动加载:
通过Class.forName()加载数据库驱动类,也是反射典型的使用方式。
此外,反射也是调试工具、热部署、脚本引擎、模板引擎等领域的重要基础。它让 Java 在静态类型语言中具备了动态编程能力,极大拓展了语言边界。
73. 动态代理的几种方式
回答:
Java 动态代理主要有两种方式:JDK 动态代理和 CGLIB 动态代理。前者基于接口实现,后者基于子类继承。二者适用范围与性能表现各有差异。
分析:
动态代理是一种在运行时动态生成代理类并实现方法增强的技术。它广泛应用于 Spring AOP、事务管理、远程调用、权限控制等场景。Java 中主要有两种动态代理机制:
1. JDK 动态代理
基于 java.lang.reflect.Proxy 实现,仅支持代理接口。其原理是在运行时生成一个实现目标接口的代理类,并将方法调用委托给 InvocationHandler 实现类。适用于目标类实现了接口的情况。
SomeInterface proxy = (SomeInterface) Proxy.newProxyInstance(
loader, interfaces, new MyInvocationHandler(target));优点:API 原生支持,性能优于 CGLIB(在接口场景)
限制:只能代理接口,不能代理普通类
2. CGLIB 动态代理
基于 ASM 字节码技术,通过生成目标类的子类进行方法增强,因此可代理没有实现接口的类。Spring AOP 中如果目标类未实现接口,默认使用 CGLIB。
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(TargetClass.class);
enhancer.setCallback(new MethodInterceptorImpl());
TargetClass proxy = (TargetClass) enhancer.create();优点:无接口限制,适用范围更广
限制:不能代理 final 类或 final 方法,启动速度较慢,内存开销略大
补充:
在 Spring Boot 中,默认如果类实现了接口,则使用 JDK 动态代理;否则使用 CGLIB。
总结:
- 实现了接口 → JDK 动态代理
- 没有接口 → CGLIB
- 强调性能 → 尽量使用 JDK 代理
动态代理机制是 Java 框架开发中的核心工具之一,理解其原理是掌握 AOP、RPC 等技术的基础。