跳到主要内容 JavaSE 反射与动态代理 | 极客日志
Java java
JavaSE 反射与动态代理 Java 反射与动态代理的核心概念及实现。反射允许程序在运行时检查类结构、动态创建对象及调用方法,涉及 Class、Method、Field 等核心 API。动态代理分为 JDK 动态代理(基于接口)和 CGLIB 动态代理(基于类),用于在不修改源代码的情况下增强方法功能。文章详细讲解了两者 API 用法、实现步骤及对比,并结合 Spring AOP、JUnit 及事务管理等场景说明了实际应用价值。
RedisGeek 发布于 2026/3/24 更新于 2026/4/18 4 浏览一、引言
在 Java 生态中,反射与动态代理是支撑框架设计的基石,无论是 Spring 中的 AOP,还是 Mybatis 的 Mapper 接口,都离不开这两项技术。在学习之前先了解反射和动态代理分别可以做什么,简单说反射赋予程序'看清类结构'的功能,通过反射可以知道类中构造方法,成员方法,成员变量等信息;动态代理则实现了'无侵入式增强',二者相辅相成,让代码具备灵活性与扩展性。
二、Java 反射:探索类的'隐藏世界'
2.1 反射的定义
在 Java 中,反射(Reflection)是一种强大的机制,允许程序在运行时检查或修改其自身的结构和行为。通过反射,你可以查询类的元数据(如类名、方法、字段、注解等),动态地进行创建对象,调用方法,访问字段等操作。
2.2 反射的核心 API
Java 反射的核心操作集中在 java.lang.reflect 包中,其中关键类包括 Class、Method、Field、Constructor,核心操作分为'获取 Class 对象''操作类成员'两步
(1) 如何获取类对象? public class User {
private String name;
public Integer age;
public User () {}
private User (String name) {
this .name = name;
}
public String getName () {
return name;
}
private void setAge (Integer age) {
this .age = age;
}
}
获取类对象有三种方式分别是:类名.class,对象.getClass(),Class.forName('全类名')
Class<?> class1 = User.class;
System.out.println(class1);
User user = new User ();
Class<?> class2 = user.getClass();
System.out.println(class2);
Class<?> class3 = Class.forName("User" );
System.out.println(class3);
System.out.println(class1.equals(class2));
(2) 利用反射操作类 得到类成员后就可以利用反射操作类的构造方法,成员方法,成员变量等元数据 ,核心 API 也很易懂
Class<User> userClass = User.class;
Constructor<?>[] publicConstructors = userClass.getConstructors();
System.out.println(Arrays.toString(publicConstructors));
Constructor<?>[] declaredConstructors = userClass.getDeclaredConstructors();
System.out.println(Arrays.toString(declaredConstructors));
Constructor<User> constructor = userClass.getConstructor();
System.out.println(constructor);
Constructor<User> declaredConstructor = userClass.getDeclaredConstructor(String.class);
System.out.println(declaredConstructor);
Constructor<User> declaredConstructor = userClass.getDeclaredConstructor(String.class);
System.out.println(declaredConstructor);
int modifiers = declaredConstructor.getModifiers();
System.out.println(declaredConstructor + "的构造方法的修饰符为" + modifiers);
Class<?>[] types = declaredConstructor.getParameterTypes();
System.out.println(declaredConstructor + "的形参类型有" + Arrays.toString(types));
declaredConstructor.setAccessible(true );
User user = declaredConstructor.newInstance("test" );
System.out.println(user.getName());
在 Java 中,java.lang.reflect.Constructor 类的 getModifiers() 方法返回一个整数,该整数是构造器修饰符的位掩码(Bitmask)。每个修饰符对应一个二进制位,不同修饰符通过按位或(OR)组合成最终的整数值。
获取字段,方法等 API 与获取构造器类似,演示一下通过反射操作私有成员
Class<User> userClass = User.class;
Constructor<User> privateConstructor = userClass.getDeclaredConstructor(String.class);
privateConstructor.setAccessible(true );
User user = privateConstructor.newInstance("test" );
Method setAge = userClass.getDeclaredMethod("setAge" , Integer.class);
setAge.setAccessible(true );
setAge.invoke(user, 18 );
System.out.println(user.getName() + "的年龄为" + user.getAge());
Field nameField = userClass.getDeclaredField("name" );
nameField.setAccessible(true );
String name = (String) nameField.get(user);
System.out.println("利用反射获取的私有属性" + name);
Method 类中 invoke() 方法比较常用,传递的参数表示 1:调用方法的对象,2:调用方法传递的参数;返回值就是原方法返回值
三、动态代理,无侵入式增强的实现利器
3.1 动态代理定义 动态代理是一种设计模式,允许程序在运行时动态生成代理类,代理类会包裹目标对象,在不修改目标代码的情况下,对目标方法进行增强 。Java 中动态代理主要分为两种JDK 动态代理和 CGLIB 动态代理 。
3.2 JDK 动态代理 (接口增强)
(1) 核心组件
InvocationHandler :定义代理逻辑,所有代理方法的调用都会委托给该接口的 invoke 方法。
Proxy :用于创建动态代理类和实例的工厂
(2) 实现步骤
定义接口:目标类需实现接口
实现 InvocationHandler:处理方法调用逻辑
生成代理对象:通过 Proxy.newProxyInstance() 创建代理
(3) 示例代码
public interface UserService {
void addUser (String name) ;
void deleteUser (Integer id) ;
}
public class UserServiceImpl implements UserService {
@Override
public void addUser (String name) {
System.out.println("执行添加姓名为 : " + name + " 用户的操作" );
}
@Override
public void deleteUser (Integer id) {
System.out.println("执行删除 id 为 : " + id + " 用户的操作" );
}
}
实现 InvocationHandler:处理方法调用逻辑
public class LogInvocationHandler implements InvocationHandler {
private Object target;
public LogInvocationHandler (Object target) {
this .target = target;
}
@Override
public Object invoke (Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("【日志】开始调用方法 : " + method.getName() + ",方法参数为 : " + Arrays.toString(args));
Object result = method.invoke(target, args);
System.out.println("【日志】方法调用结束 : " + method.getName());
return result;
}
}
生成代理对象:通过 Proxy.newProxyInstance() 创建代理
public static void main (String[] args) {
UserServiceImpl target = new UserServiceImpl ();
LogInvocationHandler handler = new LogInvocationHandler (target);
UserService proxy = (UserService) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
handler
);
proxy.addUser("test" );
proxy.deleteUser(1001 );
}
【日志】开始调用方法 : addUser,方法参数为 :[test ] 执行添加姓名为 : test 用户的操作 【日志】方法调用结束 : addUser 【日志】开始调用方法 : deleteUser,方法参数为 :[1001] 执行删除 id 为 :1001 用户的操作 【日志】方法调用结束 : deleteUser
(4) 核心 API 用法
public Object invoke(Object proxy, Method method, Object[] args):三个参数分别是代理对象本身(通常不需要使用);被调用的目标方法;方法参数数组。
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h):里面要传递三个参数分别是类加载器(通常使用目标接口的类加载器);目标接口数组(至少一个接口);代理逻辑处理器。
3.3 CGLIB 实现动态代理 (类增强)
(1)核心组件
MethodInterceptor :拦截目标类的方法调用,定义增强逻辑
Enhancer :生成代理类和实例的工具
(2) 实现步骤
引入 CGLIB 依赖
定义无接口的目标类
实现 MethodInterceptor
生成 CGLIB 代理对象:利用 Enhancer.create() 方法
(3) 示例代码 <dependency >
<groupId > cglib</groupId >
<artifactId > cglib</artifactId >
<version > 3.3.0</version >
</dependency >
public class OrderService {
public void createOrder (String orderNo) {
System.out.println("创建订单,订单号为 : " + orderNo);
}
}
自定义类实现 MethodInterceptor,处理方法调用逻辑
public class TransactionMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept (Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("【事务】开启数据事务" );
Object result = methodProxy.invokeSuper(o, objects);
System.out.println("【事务】提交数据事务" );
return result;
}
}
通过 Enhancer.create() 生成代理对象
public class CglibProxyDemo {
public static void main (String[] args) {
Enhancer enhancer = new Enhancer ();
enhancer.setSuperclass(OrderService.class);
enhancer.setCallback(new TransactionMethodInterceptor ());
OrderService proxy = (OrderService) enhancer.create();
proxy.createOrder("order_112233" );
}
}
【事务】开启数据事务 创建订单,订单号为 : order_112233 【事务】提交数据事务
(4) 核心 API 用法
public void setSuperclass(Class<?> superclass); 指定目标类
public void setCallback(Callback callback); 设置拦截器
public Object create(); 生成代理实例
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy):参数分别是 obj:代理对象(通常不需要使用);method:被调用的目标方法;args:方法参数数组;proxy:MethodProxy 对象,比反射调用更高效
3.3 JDK 动态代理和 CGLIB 动态代理横向对比 组件 JDK 动态代理 CGLIB 动态代理 接口 InvocationHandler MethodInterceptor 生成代理方式 Proxy.newProxyInstance Enhancer.create() 目标类型 接口 类 调用目标方法 method.invoke(target, args) proxy.invokSuper(obj, args)
四、反射与动态代理的实际应用
(1) 反射的实际应用
Spring 框架 :Spring 使用反射来创建和管理 Bean。例如,通过配置文件(如 XML 或注解)指定要创建的类,Spring 容器利用反射机制根据类名动态加载类,并调用其构造函数创建对象实例。
JUnit :JUnit 利用反射来发现和执行测试方法。它通过反射遍历测试类中的方法,根据特定的注解(如@Test)识别出测试方法,并动态调用这些方法来执行测试。例如,在一个测试类中定义了多个测试方法,JUnit 通过反射获取这些方法并依次执行,从而实现对代码的自动化测试。
(2) 动态代理的实际应用
Spring AOP :Spring AOP 大量使用动态代理来实现切面功能。例如,在一个企业级应用中,可能需要对业务方法进行日志记录、事务管理等操作。通过动态代理,可以在不修改目标业务类代码的前提下,为目标方法添加这些额外的功能。Spring 会为目标对象创建动态代理对象,当调用代理对象的方法时,会先执行切面逻辑(如记录日志),然后再调用目标对象的实际方法,最后还可以在方法调用后执行一些清理或补充逻辑(如事务提交)。
在数据库事务管理 中,动态代理可以用于管理事务的开启、提交和回滚。以一个银行转账操作的业务方法为例,动态代理可以在方法调用前开启事务,方法执行过程中如果没有异常则提交事务,若出现异常则回滚事务。这样可以将事务管理的逻辑从业务代码中分离出来,提高代码的可维护性和复用性。
通过上面的例子可以初步了解反射与动态代理,想要深度学习的话可以多看看框架中的源码,学习优秀的代码风格以及设计模式。
微信扫一扫,关注极客日志 微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
相关免费在线工具 Keycode 信息 查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
Escape 与 Native 编解码 JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
JavaScript / HTML 格式化 使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
JavaScript 压缩与混淆 Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
Base64 字符串编码/解码 将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
Base64 文件转换器 将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online