动态代理与反射
在 Java 编程中,反射(Reflection)和动态代理(Dynamic Proxy)是两个强大的特性,它们共同为框架设计、AOP(面向切面编程)等场景提供了底层支持。下面详细解析这两个概念及其关联。
一、反射(Reflection)
反射是 Java 提供的一种能力,允许程序在运行时获取类的信息(如类名、方法、字段等),并动态调用类的方法或操作字段,而无需在编译期知道具体的类信息。
核心功能:
- 获取类的元数据(类名、父类、接口、方法、字段等)
- 动态创建对象实例
- 动态调用方法
- 动态访问 / 修改字段值
关键类:
Class
:代表类的字节码对象,是反射的入口Constructor
:类的构造方法Method
:类的方法Field
:类的字段
示例代码:
import java.lang.reflect.Method;public class ReflectionDemo {public static void main(String[] args) throws Exception {// 1. 获取Class对象Class<?> clazz = Class.forName("java.lang.String");// 2. 创建实例Object strObj = clazz.getConstructor(String.class).newInstance("Hello Reflection");// 3. 获取并调用方法Method lengthMethod = clazz.getMethod("length");int length = (int) lengthMethod.invoke(strObj);System.out.println("字符串长度: " + length); // 输出: 18}
}
应用场景:
- 框架开发(如 Spring 的 IOC 容器)
- 序列化与反序列化
- 注解处理器
- ORM 框架(如 MyBatis)
二、动态代理(Dynamic Proxy)
动态代理是一种在运行时动态生成代理类的技术,它允许在不修改原始类代码的情况下,为原始类的方法添加额外功能(如日志、事务、权限控制等)。
Java 中动态代理主要有两种实现方式:
- JDK 动态代理:基于接口实现,必须面向接口
- CGLIB 动态代理:基于继承实现,可以代理类(需引入第三方库)
JDK 动态代理核心组件:
InvocationHandler
:代理逻辑接口,定义了代理对象调用方法时的处理逻辑Proxy
:生成代理类的工具类
JDK 动态代理示例:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;// 1. 定义接口
interface UserService {void addUser(String username);
}// 2. 实现接口
class UserServiceImpl implements UserService {@Overridepublic void addUser(String username) {System.out.println("添加用户: " + username);}
}// 3. 实现InvocationHandler
class LogInvocationHandler implements InvocationHandler {private Object target; // 被代理的目标对象public LogInvocationHandler(Object target) {this.target = target;}@Overridepublic 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. 测试动态代理
public class DynamicProxyDemo {public static void main(String[] args) {// 创建目标对象UserService userService = new UserServiceImpl();// 创建代理对象UserService proxy = (UserService) Proxy.newProxyInstance(UserService.class.getClassLoader(),new Class[]{UserService.class},new LogInvocationHandler(userService));// 调用代理对象方法proxy.addUser("张三");}
}
执行结果:
日志: 方法addUser开始执行
添加用户: 张三
日志: 方法addUser执行结束
三、反射与动态代理的关系
动态代理的实现依赖于反射机制:
- JDK 动态代理在运行时通过
Proxy
类动态生成代理类的字节码 - 当调用代理对象的方法时,会通过反射调用
InvocationHandler
的invoke
方法 - 在
invoke
方法内部,通过反射(Method.invoke()
)调用目标对象的原始方法
简单来说:动态代理是反射的一种高级应用,它利用反射实现了方法的动态调用和增强。
四、两种动态代理的对比
特性 | JDK 动态代理 | CGLIB 动态代理 |
---|---|---|
实现方式 | 基于接口 | 基于继承 |
依赖 | JDK 内置 | 第三方库(需引入) |
代理目标 | 只能代理接口 | 可以代理类和接口 |
性能 | 调用时稍慢 | 生成代理类时稍慢,调用时更快 |
局限性 | 必须有接口 | 不能代理 final 类和方法 |
五、实际应用场景
- AOP 编程:如 Spring AOP 中使用动态代理实现横切逻辑(日志、事务等)
- 远程调用:如 RMI 中使用动态代理处理网络通信细节
- 权限控制:在方法调用前验证权限
- 延迟加载:在真正需要时才初始化目标对象
通过反射和动态代理,Java 程序获得了更强的灵活性和扩展性,尤其在框架设计中发挥着重要作用。