反射与代理

反射

定义

java 语言提供的一种能力,允许程序在运行时(而非编译时)获取类的信息(如类的名称、方法、属性等),

并能动态创建类的实例、调用类的方法、访问或修改类的属性等。

这种机制就像 “反射” 一样,能反向探知类的内部结构,因此得名 “反射机制”。

用途

  • 框架底层实现
  • 动态代理与AOP
  • 工具类
  • 序列化与反序列化

一、获取Class对象四种方法

我们要实现反射,需要通过获取class来获得类的属性与方法信息。

1.通过类的class属性获得

1
Class<Person> personClass = Person.class;

2.通过Class.forName()方法获得

1
Class<?> aClass = Class.forName("cn.darven.Person");

3.通过实例化对象的getClass()方法

1
2
Person person = new Person();
Class<? extends Person> aClass = person.getClass();

4.通过类加载器获得

1
Class<?> aClass1 = ClassLoader.getSystemClassLoader().loadClass("cn.darven.Person");

以上就是获得class对象的四个方法,这个class对象有很多方法,分别获得类的属性/方法信息。接下来我们就来介绍这些常用的方法。

二、Class 类的常用方法(获取类信息 / 结构)

Class 类提供了大量方法用于获取类的元信息(名称、父类、接口、构造器、方法、字段等)。

1. 获取类基本信息

  • String getName():返回类的全限定名(如 java.lang.String)。
  • String getSimpleName():返回类的简单名称(如 String)。
  • Class<?> getSuperclass():返回父类的 Class 对象(如 Object.class 是所有类的父类)。
  • Class<?>[] getInterfaces():返回类实现的所有接口的 Class 数组。
  • int getModifiers():返回类的修饰符(如 publicabstractfinal 等,需配合 Modifier 工具类解析)。

2. 获取构造器(返回 Constructor 对象)

  • Constructor<?>[] getConstructors():获取类中所有 public 构造器(不包括私有构造器)。
  • Constructor<?> getConstructor(Class<?>... parameterTypes):获取指定参数类型的 public 构造器(参数为构造器参数的 Class 数组)。
  • Constructor<?>[] getDeclaredConstructors():获取类中 所有构造器(包括 private、protected 等,不限制访问权限)。
  • Constructor<?> getDeclaredConstructor(Class<?>... parameterTypes):获取指定参数类型的 任意访问权限构造器

3. 获取方法(返回 Method 对象)

  • Method[] getMethods():获取类中所有 public 方法(包括从父类继承的 public 方法)。
  • Method getMethod(String name, Class<?>... parameterTypes):获取指定名称和参数类型的 public 方法(name 是方法名,parameterTypes 是参数的 Class 数组)。
  • Method[] getDeclaredMethods():获取类中 所有方法(包括 private、protected 等,仅当前类声明的,不包括父类)。
  • Method getDeclaredMethod(String name, Class<?>... parameterTypes):获取指定名称和参数类型的 任意访问权限方法(仅当前类声明)。

4. 获取字段(返回 Field 对象)

  • Field[] getFields():获取类中所有 public 字段(包括从父类继承的 public 字段)。
  • Field getField(String name):获取指定名称的 public 字段(name 是字段名)。
  • Field[] getDeclaredFields():获取类中 所有字段(包括 private、protected 等,仅当前类声明的,不包括父类)。
  • Field getDeclaredField(String name):获取指定名称的 任意访问权限字段(仅当前类声明)。

5. 其他常用方法

  • boolean isInterface():判断当前类是否是接口。
  • boolean isArray():判断当前类是否是数组。
  • Object newInstance():通过类的 无参 public 构造器 创建实例(JDK 9 后 deprecated,推荐用 Constructor.newInstance())。

代理

代理模式是一种结构型设计模式,核心思想是通过代理对象间接访问目标对象,从而在不修改目标对象代码的前提下,对目标对象的方法进行增强(如日志记录、权限校验、事务管理等)。根据代理对象的创建时机,可分为静态代理动态代理,其中动态代理又以JDK 动态代理CGLIB 动态代理最为常用。

一、静态代理

静态代理是指代理类在编译期就已确定(与目标类同时存在),代理类和目标类通常实现同一个接口,代理类持有目标对象的引用,在调用目标方法时添加增强逻辑。

实现步骤

  1. 定义公共接口(规范目标类和代理类的方法);
  2. 实现目标类(真正的业务逻辑);
  3. 实现代理类(实现接口,持有目标对象,在方法中增强逻辑)。

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 1. 公共接口
public interface UserService {
void addUser(String name);
}
// 2. 目标类(真正的业务逻辑)
public class UserServiceImpl implements UserService {
@Override
public void addUser(String name) {
System.out.println("添加用户:" + name); // 核心业务
}
}
// 3. 代理类(增强逻辑)
public class UserServiceProxy implements UserService {
// 持有目标对象
private UserService target;
public UserServiceProxy(UserService target) {
this.target = target;
}
@Override
public void addUser(String name) {
// 增强逻辑:前置处理
System.out.println("日志:开始添加用户");
// 调用目标方法
target.addUser(name);
// 增强逻辑:后置处理
System.out.println("日志:用户添加完成");
}
}
// 使用
public class Main {
public static void main(String[] args) {
UserService target = new UserServiceImpl();
UserService proxy = new UserServiceProxy(target);
proxy.addUser("张三"); // 通过代理调用
}
}

优缺点

  • 优点:简单直观,无需依赖框架,增强逻辑清晰。
  • 缺点
    • 代理类与目标类强耦合(必须实现同一接口);
    • 若接口方法增多,代理类需同步修改,维护成本高(代码冗余);
    • 只能代理特定接口的类,灵活性差。

二、动态代理

动态代理是指代理类在运行时动态生成(编译期不存在),无需手动编写代理类代码,可灵活代理任意类(或接口),大幅减少代码冗余。常见实现方式有两种:JDK 动态代理CGLIB 动态代理

1. JDK 动态代理(基于接口)

JDK 动态代理是 Java 原生支持的代理方式,必须基于接口实现,核心依赖 java.lang.reflect.Proxy 类和 InvocationHandler 接口。

实现原理
  • 核心逻辑:通过 Proxy.newProxyInstance() 方法在运行时动态生成代理类的字节码,该代理类实现了目标对象的所有接口,并持有一个 InvocationHandler 实例。
  • 调用流程:当调用代理对象的方法时,会自动转发到 InvocationHandler 的 invoke() 方法,在 invoke() 中可自定义增强逻辑,并通过反射调用目标对象的方法。
关键类 / 接口
  • Proxy:生成代理类的工具类,核心方法 newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h),返回动态生成的代理对象。
    • loader:类加载器(通常用目标类的类加载器);
    • interfaces:目标对象实现的接口数组;
    • h:InvocationHandler 实例(增强逻辑的载体)。
  • InvocationHandler:接口中仅一个方法 invoke(Object proxy, Method method, Object[] args),用于定义增强逻辑:
    • proxy:代理对象本身(慎用,避免循环调用);
    • method:目标方法的 Method 对象(通过反射获取);
    • args:目标方法的参数数组;
    • 返回值:目标方法的返回值。
示例代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// 1. 公共接口(同上)
public interface UserService {
void addUser(String name);
}
// 2. 目标类(同上)
public class UserServiceImpl implements UserService {
@Override
public void addUser(String name) {
System.out.println("添加用户:" + name);
}
}
// 3. 定义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() + "方法开始执行");
// 反射调用目标方法
Object result = method.invoke(target, args);
// 增强逻辑:后置处理
System.out.println("日志:" + method.getName() + "方法执行结束");
return result;
}
}
// 4. 生成代理对象并使用
public class Main {
public static void main(String[] args) {
UserService target = new UserServiceImpl();
// 生成代理对象
UserService proxy = (UserService) Proxy.newProxyInstance(
target.getClass().getClassLoader(), // 类加载器
target.getClass().getInterfaces(), // 目标接口
new LogInvocationHandler(target) // 增强逻辑
);
proxy.addUser("张三"); // 通过代理调用
}
}
特点
  • 必须基于接口(代理类实现接口,Java 不支持多继承,无法继承目标类);
  • 无需依赖第三方库(JDK 自带);
  • 代理类在运行时动态生成(类名通常为 $Proxy0、$Proxy1 等);
  • 性能:JDK 8 及以上优化后性能较好,接近 CGLIB。

2. CGLIB 动态代理(基于继承)

CGLIB(Code Generation Library)是一个第三方字节码生成库,基于继承实现动态代理(无需接口),通过动态生成目标类的子类作为代理类,并重写目标方法添加增强逻辑。

实现原理
  • 核心逻辑:借助 ASM 字节码框架,在运行时动态生成目标类的子类(代理类),子类重写目标类的非 final 方法,在重写方法中添加增强逻辑,并通过 MethodProxy 调用父类(目标类)的方法。
  • 关键前提:目标类不能是 final(否则无法继承),目标方法不能是 final(否则无法重写)。
关键类 / 接口
  • Enhancer:CGLIB 的核心类,用于生成代理类,通过 setSuperclass() 指定父类(目标类),setCallback() 设置增强逻辑(通常用 MethodInterceptor)。
  • MethodInterceptor:接口中仅一个方法 intercept(Object obj, Method method, Object[] args, MethodProxy proxy),用于定义增强逻辑:
    • obj:代理对象(子类实例);
    • method:目标方法的 Method 对象;
    • args:目标方法的参数数组;
    • proxy:MethodProxy 对象(用于高效调用父类方法);
    • 返回值:目标方法的返回值。
示例代码(需引入 CGLIB 依赖)

Maven 依赖:

1
2
3
4
5
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 1. 目标类(无需实现接口)
public class UserService { // 非final类
public void addUser(String name) { // 非final方法
System.out.println("添加用户:" + name);
}
}
// 2. 定义MethodInterceptor(增强逻辑)
public class LogMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
// 增强逻辑:前置处理
System.out.println("日志:" + method.getName() + "方法开始执行");
// 调用父类(目标类)的方法(比反射更高效)
Object result = proxy.invokeSuper(obj, args);
// 增强逻辑:后置处理
System.out.println("日志:" + method.getName() + "方法执行结束");
return result;
}
}
// 3. 生成代理对象并使用
public class Main {
public static void main(String[] args) {
// 创建Enhancer实例
Enhancer enhancer = new Enhancer();
// 设置父类(目标类)
enhancer.setSuperclass(UserService.class);
// 设置增强逻辑
enhancer.setCallback(new LogMethodInterceptor());
// 生成代理对象(子类实例)
UserService proxy = (UserService) enhancer.create();
// 调用代理方法
proxy.addUser("张三");
}
}
特点
  • 无需接口,可代理任意非 final 类;
  • 基于字节码生成技术(直接操作 class 文件),性能在早期版本优于 JDK 动态代理(JDK 8 后差距缩小);
  • 依赖第三方库(CGLIB);
  • 代理类是目标类的子类(类名通常为 目标类名$$EnhancerByCGLIB$$随机字符串)。

三、JDK 动态代理 vs CGLIB 动态代理

对比维度 JDK 动态代理 CGLIB 动态代理
底层原理 基于接口(实现目标接口) 基于继承(生成目标类的子类)
依赖 JDK 原生(无第三方依赖) 需引入 CGLIB 库
代理条件 目标类必须实现接口 目标类不能是 final,方法不能是 final
性能(JDK 8+) 较好(反射优化后) 接近 JDK 动态代理
灵活性 仅能代理接口实现类 可代理任意非 final 类

四、动态代理的典型应用

  • Spring AOP:默认对实现接口的类使用 JDK 动态代理,对无接口的类使用 CGLIB(可通过配置强制使用 CGLIB);
  • MyBatis:Mapper 接口的实现类通过 JDK 动态代理生成,无需手动编写实现类;
  • RPC 框架:如 Dubbo,通过动态代理生成服务接口的代理类,实现远程调用的封装;
  • 权限框架:如 Shiro,通过动态代理对方法调用进行权限校验。

总结:静态代理适合简单场景,但灵活性差;动态代理通过运行时生成代理类,大幅提升灵活性,是框架设计的核心技术之一,需根据是否有接口、是否依赖第三方库等选择 JDK 或 CGLIB 实现。


反射与代理
https://darven-cs.github.io/2025/10/10/反射与代理/
作者
Darven
发布于
2025年10月10日
许可协议